【发布时间】:2011-12-13 17:31:26
【问题描述】:
问题如下:
- 输入:来自维基百科的所有文章(33gb 文本)
- 输出:SQLite 文件中来自 Wikipedia 的每个单词 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#):
创建
Dictionary<string,int>以计算令牌。将标记添加到此字典,直到它变得太大而无法放入 RAM。
-
将字典中的标记插入(不更新)到没有索引的临时表中。该表具有以下架构:
CREATE TABLE [temp] ([token] TEXT, [count] INTEGER) 如果还有更多的token,清空字典,进入第2步。
-
将令牌从临时表复制到令牌表:
INSERT INTO [tokens] SELECT [token], SUM([count]) AS [count] FROM [temp] GROUP BY [token]
此方法“仅”需要 24 小时来处理数据集,但我认为这不是最好的方法,因为第 5 步需要 24 小时中的 22 小时。
您知道可以解决此问题的替代方法吗?
附:我的应用程序是单线程的,我在事务中分批进行上述插入(每批 100000 个)。
【问题讨论】:
标签: c# performance algorithm sqlite