Java 如何在有限的内存中处理大量的小文件?
以下是问题的描述: 我在一个目录中有大量的小日志文件,假设:Java 如何在有限的内存中处理大量的小文件?,java,multithreading,java.util.concurrent,Java,Multithreading,Java.util.concurrent,以下是问题的描述: 我在一个目录中有大量的小日志文件,假设: 所有文件都遵循命名约定:yyyy-mm-dd.log,例如:2013-01-01.log、2013-01-02.log 大约有1000000个小文件 所有文件的总大小为数TB 现在,我必须为每个文件中的每一行预先准备一个行号,行号是累积的,分布在文件夹中的所有文件中(文件按时间戳排序)。例如: 2013-01-01.log中,行号为1~2500 2013-01-02.log,行号从2501到7802 2016-03-26.log,
yyyy-mm-dd.log
,例如:2013-01-01.log、2013-01-02.log- 2013-01-01.log中,行号为1~2500
- 2013-01-02.log,行号从2501到7802
- 2016-03-26.log,行号从1590321~3280165
经过思考和寻找,这里是我想到的最好的。有点 很长,所以我只简单介绍一下每一步:
ConcurrentSkipListMap
,键是文件名,值是文件的行数,键按顺序排列ConcurrentSkipListMap
计算每个文件的起始行号,例如,2013-01-01.log的起始行号和行号分别为1和1500,则2013-01-02.log的起始行号为1501BufferedReader
逐行读取每个文件,预加行号,然后使用BufferedWriter
写入相应的tmp文件。创建线程池并并发处理您是否有更好的解决方案,或者对我的解决方案进行优化?提前谢谢 不确定这些问题是否符合问答的SO模式,但我尝试了一些提示来回答 事实1)考虑到1M文件和100MB的限制,几乎没有办法同时在内存中保存所有文件的信息。除了像以前我们用C语言编程时那样,可能会做很多小动作 事实2)我看不到一种方法可以绕过一次读取所有文件来计算行号,然后全部重写,这意味着再次读取所有文件 A) 这是家庭作业问题吗?在Java7或Java8中,可能有一种方法可以一个接一个地从文件夹惰性地生成文件名,但我不知道。如果有,就用它。如果没有,您可能需要生成文件名,而不是列出它们。这要求您可以插入开始日期和结束日期作为输入。不确定这是否可行 B) 如果有一个惰性的
迭代器
,无论是从jdk列出文件还是从自我实现生成文件名,都可以使用N个迭代器将工作划分为N个线程
C) 现在,每个线程负责自己的文件切片,读取它们并只保留其切片的总行数
D) 根据每个切片的总数,计算每个切片的起始编号
E) 再次在N个线程上分发迭代器以进行行编号。在编写tmp文件后立即重命名该文件,不要等待所有操作完成,以免再次迭代所有文件
在每个时间点,存储在内存中的信息都非常小:每个线程一个文件名,整个切片上的行数,正在读取的文件的当前行数。如果N不是非常大的话,100MB就足够了
EDIT:this
Files.find()
是惰性填充的,但我无法轻松找到它背后的代码(Java 8中的一些DirectoryStream
),以查看懒散是否只涉及一次读取一个文件夹的全部内容,或者是否确实一次读取一个文件名。或者这是否取决于所使用的文件系统。对于100MB限制和1M文件,第34行可能已经在logPath.toFile().listFiles()处爆炸了代码>。100 MB意味着如果你胆敢同时在内存中保留所有文件的信息,那么每个文件只能使用100字节。@Harald,谢谢。可能logPath.toFile().list()
会消耗更少的内存。另外,有人建议在Java7中使用Files.walkFileTree
。我将尝试这两种方法,但问题是我无法创建这么多测试日志。A)这是一个真正问题的家庭作业,文件名可能不连续,很难根据需要生成。B) 事实上,我和你的想法是一样的。E) 我不会在编写tmp文件后立即重命名它,因为我会让它稍后同时完成,我希望它会更快。谢谢你,哈拉尔德!