我需要读取一组文件并将其分解为键值对,并将这些文件保存为光盘上该键的(键,值列表),就像map-reduce范例一样。但是一切都在一台电脑上。例如,我可以在不同文件上写入不同的列表,并使用密钥命名文件。这似乎是一种非常糟糕的做事方式。首先,如果你有十亿个密钥,你将得到十亿个文件。显然这不会起作用,我需要某种内存映射。我还必须有不同的线程来做map工作,所以如果他们要写入这个相同的缓冲区,它们之间必须有某种同步。如果我有一个键值缓冲区映射,并通过缓冲区同步,那么线程不应该踩到彼此的脚趾,所以我认为该部分应该工作。问题是如何将值映射到光盘。如何编写与同一文件中不同键对应的缓冲区?如果有人能指出我正确的方向,我将不胜感激。我对这方面的了解非常可怜。再次感谢。
答案 0 :(得分:5)
从实际的角度来看,使用BerkeleyDB很容易做到这一点as Lirik suggested.
如果你对理论比对实践更感兴趣,我建议你将其作为“外部排序”操作。也就是说,尽可能多地读入内存中的输入,然后按键排序。将排序的块作为单个文件写出。然后可以将排序后的文件轻松合并到一个文件中。
在其他应用程序中,这是Lucene用于构建用于搜索文本的“倒排索引”的方法。 “键”是文档中的单词,“值”是单词出现的文档列表。 Lucene读取文档,并为每个单词在内存中创建一个术语到文档的条目。当内存已满时,它会将索引段写入磁盘。当磁盘上有很多索引段时,它们会合并为一个段。事实上,你也可以让Lucene的索引编写器适应你的任务。
可以将工作划分为多个线程。但是,您必须对磁盘争用敏感。跳过同时读取和写入许多文件将大大减慢传统驱动器的速度。可能有机会同时安排一些活动。在将先前排序的块写入磁盘时,您可能会从一个文件中读取新数据,尤其是在计算机有两个磁盘驱动器的情况下。当然,使用SSD临时存储某些已排序的段会有很大帮助。
答案 1 :(得分:4)
我认为Oracle's Berkeley DB可能只适合你:
Berkeley DB旨在将数据存储为在一个可用访问方法中索引的键/值对中的不透明字节数据数组,如上所示。
伯克利非常强大,成熟且快速,但如果你想采用更轻量级的方法,那就使用SQLite。
另一种选择是使用Google的LevelDB;它是用C ++编写的,但有Java wrappers around it。 LevelDB非常快速且非常轻量级!
如果没有关于项目的更多细节,我只能说:
批判: - 我不确定你的意思是“键值缓冲区映射”......你是否将缓冲区映射到每个键?你为什么需要那个?
答案 2 :(得分:0)
您是否看过使用Hadoop?
答案 3 :(得分:0)
Chronicle Map应该是解决此问题的好方法。
一般来说,无论是在操作速度还是消耗内存方面,它都是非常有效的。即它之前的建议比BerkeleyDB much faster。
Chronicle Map是一个分段存储,允许并行处理段,例如: G:
for (int i = 0; i < chronicleMap.segments(); i++) {
int segmentIndex = i;
executor.submit(() -> {
chronicleMap.segmentContext(segmentIndex).forEachSegmentEntry(entry -> {
// do processing with entry.key() and entry.value(),
// value() could be a List or some Iterator-like abstraction
});
});
}
请参阅MapSegmentContext
Javadocs。
但是,每个键could not always be handled efficiently with Chronicle Map具有(逻辑上)多个值。但在你的情况下,如果你只需要处理每个键的静态值集,而不是添加/删除值,它就可以正常工作。