【问题标题】:Algorithm for merging large files合并大文件的算法
【发布时间】:2010-09-12 18:50:27
【问题描述】:

我有几个事件日志文件(每行一个事件)。日志可能会重叠。日志是在可能来自多个时区的单独客户端计算机上生成的(但我假设我知道时区)。每个事件都有一个标准化为公共时间的时间戳(通过使用适合日志文件的时区实例化每个日志解析器日历实例,然后使用 getTimeInMillis 获取 UTC 时间)。日志已经按时间戳排序。多个事件可以同时发生,但它们绝不是相等的事件。

这些文件可能比较大,比如单个日志中有 500000 个或更多事件,因此将日志的全部内容读入一个简单的 Event[] 是不可行的。

我正在尝试将每个日志中的事件合并到一个日志中。这有点像一个合并排序任务,但每个日志都已经排序,我只需要将它们放在一起。第二个组件是可以在每个单独的日志文件中见证相同的事件,我想在文件输出日志中“删除重复事件”。

这是否可以“就地”完成,例如在每个日志文件的一些小缓冲区上按顺序工作?我不能简单地将所有文件读入 Event[],对列表进行排序,然后删除重复项,但到目前为止,我有限的编程能力只能让我将其视为解决方案。当我同时从每个日志中读取事件时,是否可以使用一些更复杂的方法来执行此操作?

【问题讨论】:

  • 哦,伙计! “合并排序”是否敲响了警钟? ;)

标签: java sorting file merge


【解决方案1】:

当然 - 打开每个日志文件。将每一行的第一行读入一个“当前”行数组。然后从当前数组中重复选择时间戳最低的行。将其写入输出,并从相应的源文件中读取一个新行来替换它。

这是一个 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

【讨论】:

    【解决方案2】:

    我们需要按时间顺序合并多个日志文件,每个日志条目具有多行(Java 应用程序经常这样做 - 它们的堆栈跟踪是相同的)。我决定实现简单的 shell+perl 脚本。它涵盖了我们的任务。如果您对此感兴趣 - 请点击链接http://code.google.com/p/logmerge/

    【讨论】:

      【解决方案3】:

      查看此链接:http://www.codeodor.com/index.cfm/2007/5/10/Sorting-really-BIG-files/1194

      • 使用堆(基于数组)。此堆/数组中的元素数将等于您拥有的日志文件数。

      • 从所有文件中读取第一条记录并将它们插入到您的堆中。

      • 循环直到(任何文件中都没有更多记录)

      > 从堆中删除最大元素 > 将其写入输出 > 从(前一个)最大元素所属的文件中读取下一条记录 如果该文件中没有更多记录 从文件列表中删除它 继续 > 如果它与(前一个)max 元素不同,则将其添加到堆中

      现在您将所有事件都保存在一个日志文件中,它们已排序,并且没有重复。该算法的时间复杂度为 (n log k),其中 n 是记录总数,k 是日志文件的数量。

      在读取和读取文件时,您应该使用缓冲读取器和缓冲写入器对象,以最大限度地减少磁盘读取和写入次数,从而优化时间。

      【讨论】:

        【解决方案4】:

        或者您可以从 Awstats 借用一个日志合并实用程序,它是一个开源网站统计工具。

        logresolvemerge.pl是一个可以合并多个日志文件的perl脚本:你甚至可以使用多个线程来合并日志文件(需要有perl 5.8才能多线程使用)。您为什么不尝试使用现成的工具而不是构建一个?

        【讨论】:

        • 该项目是Java,我真的不想进行系统调用或需要perl。那,而且我不会将信息写入日志文件,我会处理合并的结果,因为它们是定期从合并中输出的。那是一个爱好项目,我想自己学习如何做。
        【解决方案5】:

        一次只能从两个源文件中读取一行。 比较这些行并将较旧的行写入输出文件(并前进到下一行)。 执行此操作,直到您到达两个文件的末尾并合并文件。

        并确保删除重复项:)

        我猜 C# 中的这段代码可以说明这种方法:

                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,但它应该看起来相似..

        【讨论】:

          【解决方案6】:
          1. 读取每个日志文件的第一行

          2. 循环

            一个。找到“最早的”行。

            b.将“最早”行插入主日志文件

            c。从包含最早行的文件中读取下一行

          您可以检查 b 和 c 之间的重复项,为每个文件前进指针。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-08
            • 2021-05-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多