标准的去重方法是对文件进行排序,然后顺序遍历以删除重复项。排序 5 亿行并不容易,但肯定可行。几年前,我每天都会在一台 16GB 的机器上对 50 到 100 GB 的数据进行排序处理。
顺便说一下,您可能可以使用现成的程序来完成此操作。当然,GNU sort 工具可以对大于内存的文件进行排序。我从未尝试过对 500 GB 的文件进行排序,但您可以尝试一下。您可以与
GNU Core Utilities 一起下载该实用程序。该实用程序具有“--unique”选项,因此您应该能够只需使用“sort --unique input-file > output-file”即可完成操作。它使用了类似于我下面描述的技术。建议您先在 100 MB 的文件上尝试,然后逐步增加到更大的文件。
使用 GNU sort 和我下面描述的技术,如果输入和临时目录位于不同的物理磁盘上,则性能将大大提高。将输出放置在第三个物理磁盘上或与输入放置在同一物理磁盘上。您要尽可能减少 I/O 冲突。
也许有一些商业(即付费)程序可以进行排序。开发一个能高效排序大型文本文件的程序是一项不容易的任务。如果你可以花几百美元买到某些东西,而你的时间也很宝贵,那么你可能赚了。
如果您不能使用现成的程序,那么...
如果您的文本内容分布在多个较小的文件中,则问题更容易解决。您可以先对每个文件进行排序,从这些文件中删除重复项,并编写已删除重复项的排序临时文件。然后运行简单的n路归并以将文件合并为一个输出文件,其中已删除重复项。
如果您只有一个文件,则首先读入尽可能多的行到内存中,对其进行排序,删除重复项并编写临时文件。您可以继续处理整个大文件。完成后,您会得到一些已排序的临时文件,然后可以将它们合并。
伪代码如下:
fileNumber = 0
while not end-of-input
load as many lines as you can into a list
sort the list
filename = "file"+fileNumber
write sorted list to filename, optionally removing duplicates
fileNumber = fileNumber + 1
你不必从临时文件中删除重复项,但如果你的唯一数据只占总数据的10%,那么不将重复项输出到临时文件中可以节省大量时间。
一旦所有临时文件都写好了,你需要将它们合并。根据你的描述,我估计你从文件中读取的每个块将包含大约2000万行左右。因此,你可能有25个临时文件需要处理。
现在你需要进行k路合并。这是通过创建一个优先队列来完成的。你打开每个文件,从每个文件中读取第一行,并将其与来自该行的文件的引用一起放入队列中。然后,你取出队列中最小的项目,并将其写入输出文件。为了删除重复项,你要跟踪你输出的前一行,并且如果新行与上一行相同,则不输出新行。
一旦你输出了这一行,你就从刚才输出的行所在的文件中读取下一行,并将该行添加到优先队列中。你继续这样做,直到你清空了所有的文件。
我之前发表了一系列关于
如何对一个非常大的文本文件进行排序的文章。它使用了我上面描述的技术。唯一不做的事情就是去重,但这只需要对输出临时文件和最终输出方法进行简单修改即可。即使没有优化,该程序的性能也相当不错。它不会创造任何速度记录,但应该能够在不到12小时内对5亿行进行排序和去重。考虑到第二次遍历仅处理总数据的一小部分(因为已从临时文件中删除了重复项),所以可能要快得多。
为了加快程序速度,您可以操作更小的块,并在您加载下一个块到内存时在后台线程中对一个块进行排序。您最终需要处理更多的临时文件,但这真的不是问题。堆操作略慢,但通过将输入和输出与排序重叠,可以获得额外的时间。您最终可以免费获得I/O。在典型的硬盘速度下,加载500GB的数据需要大约两个半到三个小时左右。
请查看文章系列。这是许多不同的,主要是小的文章,带您完成我描述的整个过程,并呈现工作代码。如果您有任何问题,请随时问我。