如何从庞大的数据集中有效地提取每个项目的计数?

时间:2011-12-13 17:31:27

标签: c# performance algorithm sqlite

问题在于:

  • 输入:来自维基百科的所有文章(33gb文本)
  • 输出:SQLite文件中来自维基百科的每个单词skipgram(n-gram,最大k跳过)的计数。

输出表架构是:

CREATE TABLE [tokens] ([token] TEXT UNIQUE NOT NULL PRIMARY KEY, [count] INTEGER  NOT NULL

天真的方法是,对于每个skipgram,我们在现有记录中的表或增量计数器中创建一个新记录:

INSERT OR REPLACE INTO [tokens] VALUES (@token, COALESCE((SELECT count FROM [tokens] WHERE token=@token), 0) + 1)

这种方法的问题是索引会不断更新,当数据库增长到几千兆时,这些更新速度非常慢。我们可以通过创建没有索引的“令牌”表并在处理结束时添加索引来解决这个问题。

问题是必须扫描表的select语句SELECT count FROM [tokens] WHERE token=@token会显着降低性能。

我到目前为止找到的最好的方法是(我正在使用C#):

  1. 创建Dictionary<string,int>以计算代币。

  2. 将令牌添加到此词典中,直到它变得太大而无法放入RAM中。

  3. 从字典中插入(不更新)令牌到没有索引的临时表。该表具有以下模式:

    CREATE TABLE [temp] ([token] TEXT, [count] INTEGER)
    
  4. 如果有更多令牌,请清除字典并转到步骤2.

  5. 将标记从临时表复制到标记表:

    INSERT INTO [tokens] SELECT [token], SUM([count]) AS [count] FROM [temp] GROUP BY [token]
    
  6. 此方法仅需24小时处理数据集,但我认为这不是最佳方法,因为步骤5需要24小时中的22小时。

    您是否知道可以解决此问题的替代方法?

    P.S。我的应用程序是单线程的,我在事务中批量生成上面的插入(每批100000)。

4 个答案:

答案 0 :(得分:1)

我建议创建另一个具有相同定义的表,将表填充到某个状态,将结果合并到主表,清除表并开始处理下一组项。

答案 1 :(得分:0)

我建议添加SET TRANSACTION ISOLATION READ UNCOMMITTED。这意味着计数可能略有下降,特别是在多个尝试同时插入/更新的线程环境中。

答案 2 :(得分:0)

如果你有很多演出......

我建议您不要计算令牌,而是将所有令牌添加到单个表中,并创建一个用于组织令牌的索引。

CREATE TABLE tokens (token TEXT);
CREATE INDEX tokens_token ON tokens (token ASC);

然后一次添加所有令牌......

INSERT INTO tokens VALUES ('Global Warming');
INSERT INTO tokens VALUES ('Global Cooling');

最后执行SELECT ... GROUP BY

SELECT token, COUNT(0) token_count FROM tokens GROUP BY token

答案 3 :(得分:0)

这对我来说是一个使用“计数布隆过滤器”的好地方。

它需要两次传递你的数据,它有点启发式,但它应该很快。 Bloom过滤器允许在恒定时间内进行设置插入和存在测试。计数布隆过滤器计算已找到特定值的数量,而通常的布隆过滤器仅跟踪是否存在。