合并大文件的算法
我有几个事件日志文件(每行一个事件)。日志可能会重叠。日志是在可能有多个时区的独立客户端计算机上生成的(但我想我知道该时区)。每个事件都有一个标准化为公共时间的时间戳(通过实例化每个日志解析器日历实例的时区适合该日志文件,然后使用getTimeInMillis来获取UTC时间)。日志已经按时间戳排序。多个事件可以同时发生,但绝不是相等的事件。
这些文件可能相对较大,例如单个日志中的500000个事件或者更多,因此将日志的全部内容读取到简单的Event []中是不可行的。
我正在尝试做的是将每个日志中的事件合并到一个日志中。这有点像mergesort任务,但是每个日志都已经排序了,我只需要将它们放在一起即可。第二个组件是可以在每个单独的日志文件中见证同一事件,并且我想在文件输出日志中"删除重复的事件"。
是否可以像在其中一样"按位"按顺序在每个日志文件的一些小缓冲区上工作?我不能简单地将所有文件读入Event []中,对列表进行排序,然后删除重复项,但是到目前为止,我有限的编程能力只能使我将其视为解决方案。当我同时从每个日志中读取事件时,是否可以使用一些更复杂的方法来做到这一点?
解决方案
- 从每个日志文件中读取第一行
- 圈a。找到"最早的"行。 b。将"最早"行插入主日志文件c。从包含最早一行的文件中读取下一行
我们可以检查b和c之间是否存在重复项,以使每个文件的指针前进。
确保打开每个日志文件。将第一行中的每一行读入"当前"行的数组中。然后从当前数组中反复选择时间戳最低的行。将其写入输出,并从相应的源文件中读取新行以替换它。
这是Python中的示例,但它也可以提供良好的伪代码:
def merge_files(files, key_func): # Populate the current array with the first line from each file current = [file.readline() for file in files] while len(current) > 0: # Find and return the row with the lowest key according to key_func min_idx = min(range(len(files)), key=lambda x: return key_func(current[x])) yield current[min_idx] new_line = files[min_idx].readline() if not new_line: # EOF, remove this file from consideration del current[min_idx] del files[min_idx] else: current[min_idx] = new_line
一次仅从两个源文件中读取一行。
比较各行,并将较旧的行写入输出文件(并前进到下一行)。
执行此操作,直到到达两个文件的末尾并合并了文件。
并确保删除重复项:)
我猜这可能在Cmay中的代码说明了这种方法:
StringReader fileStream1; StringReader fileStream2; Event eventCursorFile1 = Event.Parse(fileStream1.ReadLine()); Event eventCursorFile2 = Event.Parse(fileStream2.ReadLine()); while !(fileStream1.EOF && fileStream2.EOF) { if (eventCursorFile1.TimeStamp < eventCursorFile2.TimeStamp) { WriteToMasterFile(eventCursorFile1); eventCursorFile1 = Event.Parse(fileStream1.ReadLine()); } else if (eventCursorFile1.TimeStamp == eventCursorFile2.TimeStamp) { WriteToMasterFile(eventCursorFile1); eventCursorFile1 = Event.Parse(fileStream1.ReadLine()); eventCursorFile2 = Event.Parse(fileStream2.ReadLine()); } else { WriteToMasterFile(eventCursorFile1); eventCursorFile2 = Event.Parse(fileStream2.ReadLine()); } }
中断条件并不完全正确,因为这只是Quick'n'dirty,但它看起来应该相似。
或者,我们可以从Awstats借用日志合并实用程序,这是一个开源网站统计工具。
logresolvemerge.pl是一个perl脚本,可以合并多个日志文件:我们甚至可以使用多个线程合并日志文件(需要perl 5.8才能用于多线程)。我们为什么不尝试使用现成的工具而不是构建工具?
检出此链接:http://www.codeodor.com/index.cfm/2007/5/10/Sorting-really-BIG-files/1194
- 使用堆(基于数组)。该堆/数组中的元素数将等于我们拥有的日志文件数。
- 从所有文件中读取第一条记录,然后将它们插入堆中。
- 循环直到(任何文件中没有更多记录)
> remove the max element from the heap > write it to the output > read the next record from the file to which the (previous) max element belonged if there are no more records in that file remove it from file list continue > if it's not the same as the (previous) max element, add it to the heap
现在,我们将所有事件都保存在一个日志文件中,它们已排序,并且没有重复项。该算法的时间复杂度为(n log k),其中n是记录总数,k是日志文件数。
在读取文件和从文件读取文件时,应使用缓冲的读取器和缓冲的写入器对象,以最大程度地减少磁盘读写次数,以优化时间。