处理大型文件时,有哪些快速多次处理的选项?
我有一个单一文件(最小1.5 GB,但可能达到10-15 GB),需要被读取多次——大约是数百到数千次。服务器拥有大量的RAM(64+ GB)和充足的处理器(24+)。
文件将是顺序的,只读。文件在磁盘上是加密的(敏感数据)。我在读取过程中还使用MessagePack将它们反序列化为对象。
我无法将文件创建的对象存储在内存中——扩展太大了(1.5 GB的文件在内存中会变成35 GB的对象数组)。文件不能存储为字节数组(受限于Java的数组长度为2^32-1)。
我的初步想法是使用内存映射文件,但这也有其自身的限制。
目的是将文件从磁盘读取到内存中进行处理。
大量数据用于机器学习算法,需要多次读取。在计算每次文件通过时,算法本身会使用大量的堆内存,这是不可避免的,因此需要多次读取文件。
回答:
你遇到的问题是无法像同名系统调用那样使用mmap()
;系统调用可以映射到2^64,而FileChannel#map()
不能可靠地映射超过2^30的数据。
然而,你可以将FileChannel
包装到一个类中,并创建多个“映射范围”来覆盖整个文件。
我做过类似的事情,但更复杂:largetext。更复杂是因为我还需要进行解码过程,并且加载的文本必须进入内存,而你只是读取字节。较简单是因为我有一个定义的JDK接口需要实现,而你没有。
然而,你可以使用几乎相同的方法,使用Guava和RangeMap<Long, MappedByteBuffer>
。
我在上面的项目中实现了CharSequence
;我建议你实现一个LargeByteMapping
接口,取而代之,你可以从中读取你想要的任何部分;或者,嗯,任何适合你的东西。你主要的问题将是定义这个接口。我怀疑CharSequence
所做的不是你想要的。
嗯,我有朝一日可能会尝试一下,largetext
是一个非常令人兴奋的项目,这看起来像是同一类的事情;但最终更简单!
甚至可以想象一个LargeByteMapping
实现,其中一个工厂会创建这样的映射,只有一小部分在内存中,其余部分写入文件;这样的实现也会使用局部性原则:最近查询的文件部分将保留在内存中以便更快访问。
另见这里。
编辑 我觉得这里需要更多的解释… MappedByteBuffer
不会消耗堆空间!!
它只会消耗地址空间;它几乎相当于ByteBuffer.allocateDirect()
,只是它由文件支持。
这里需要做出一个非常重要的区分;以上所有的文本都假设你在读取字节,而不是字符!