【问题标题】:How to process a large number of small files in limited memory?如何在有限的内存中处理大量的小文件?
【发布时间】:2016-03-26 14:55:53
【问题描述】:

问题描述如下:

我在一个目录中有大量的小日志文件,假设:

  1. 所有文件都遵循命名约定:yyyy-mm-dd.log,例如:2013-01-01.log、2013-01-02.log。
  2. 大约有 1,000,000 个小文件。
  3. 所有文件的总大小为几 TB。

现在我必须为每个文件中的每一行添加一个行号,并且行号是累积的,分布在文件夹中的所有文件(文件按时间戳排序)中。例如:

  • 2013-01-01.log,行号1~2500
  • 在2013-01-02.log中,行号从2501~7802
  • ...
  • 在2016-03-26.log,行号从1590321~3280165

所有文件都被覆盖以包含行号。

约束是:

  1. 存储设备是 SSD,可以同时处理多个 IO 请求。
  2. CPU 足够强大。
  3. 您可以使用的总内存为 100MB。
  4. 尝试最大化应用程序的性能。
  5. 用 Java 实现和测试。

经过思考和搜索,这是我想到的最好的solutionThe code有点 很长,所以我只是对每个步骤进行简要说明:

  1. 并发统计每个文件的行数,并将映射保存到ConcurrentSkipListMap,key为文件名,value为文件行数,key为有序。

  2. 通过遍历ConcurrentSkipListMap统计每个文件的起始行号,例如2013-01-01.log的起始行号和行数分别为1和1500,那么起始行号2013-01-02.log 是 1501。

  3. 将行号添加到每个文件的每一行:使用BufferedReader逐行读取每个文件,添加行号然后使用BufferedWriter写入相应的tmp文件。创建线程池,并发处理。

  4. 同时使用线程池将所有 tmp 文件重命名为原始名称。

我已经在我的 MBP 上测试了该程序,步骤 1 和步骤 3 是预期的瓶颈。 您有更好的解决方案,还是对我的解决方案进行了一些优化?提前致谢!

【问题讨论】:

  • 由于 100MB 的限制和 1M 的文件,第 34 行很可能已经在 logPath.toFile().listFiles(); 被炸毁。 100 MB 意味着如果您敢于将所有文件的信息同时保存在内存中,那么每个文件只有 100 个字节可供使用。
  • @Harald,谢谢。也许logPath.toFile().list() 消耗更少的内存。此外,有人建议 Java 7 中的 Files.walkFileTree 可能有效。我会尝试这两个,但问题是我无法创建这么多的测试日志。

标签: java multithreading java.util.concurrent


【解决方案1】:

不确定这个问题是否适合 Q&A 的 SO 模型,但我尝试了一些提示来寻找答案。

事实 1) 鉴于 1M 文件和 100MB 限制,几乎没有办法同时将所有文件的信息保存在内存中。除了可能像过去我们用 C 编程时那样做很多摆弄。

事实 2)我没有办法绕过读取所有文件一次来计算行号,然后将它们全部重写,这意味着再次读取它们。

A) 这是一道作业题吗?在 Java 7 或 8 中,可能有一种方法可以从一个文件夹中懒惰地生成文件名,但我不知道。如果有,请使用它。如果没有,您可能需要生成文件名而不是列出它们。这将要求您可以插入开始日期和结束日期作为输入。不确定这是否可能。

B) 给定一个懒惰的Iterator<File>,无论是从jdk来列出文件还是自己实现生成文件名,都取其中的N个把工作分给N个线程。

C) 现在每个线程都处理它的文件切片,读取它们并只保留其切片的总行数。

D) 根据每个切片的总数计算每个切片的起始编号。

E) 再次将迭代器分布在 N 个线程上以进行行编号。写入后立即重命名 tmp 文件,不要等待一切完成,以免再次遍历所有文件。

在每个时间点,保存在内存中的信息都非常少:每个线程一个文件名,整个切片的行数,正在读取的文件的当前行。如果 N 不是特别大,100MB 就足够了。

编辑:Some say Files.find() 被懒惰地填充,但我无法轻易找到它背后的代码(Java 8 中的一些 DirectoryStream)来查看懒惰是否仅与读取一个文件夹的全部内容有关一次,或者是否确实一次读取一个文件名。或者这是否取决于使用的文件系统。

【讨论】:

  • A) 这是一个真正的问题的作业,文件名可能不连续,很难根据需要生成。 B)/C)/D) 我实际上和你的想法一样。 E) 我不会在 tmp 文件写入后立即重命名它,因为我让它稍后同时完成,我希望它会更快。谢谢你,哈拉尔!
猜你喜欢
  • 1970-01-01
  • 2010-09-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
相关资源
最近更新 更多