用于数据库实现的内存映射MappedByteBuffer或Direct ByteBuffer?

时间:2012-02-13 13:24:42

标签: java file-io database-design memory-mapped-files bytebuffer

由于所有上下文,这看起来像一个很长的问题。下面小说中有两个问题。感谢您抽出宝贵时间阅读本文并提供帮助。

场合

我正在开发一个可扩展的数据存储区实现,它可以支持在32位或64位系统上处理从几KB到TB或更大的数据文件。

数据存储区采用Copy-on-Write设计;始终将新数据或修改后的数据附加到数据文件的末尾,并且永远不会对现有数据进行就地编辑。

系统可以托管1个或多个数据库;每个都由磁盘上的文件表示。

实施细节并不重要;唯一重要的细节是我需要不断附加到文件并将其从KB增加到MB,再增加到GB到TB,同时随机跳过文件以进行读取操作以回答客户端请求。

第一思想

乍一看,我知道我想使用内存映射文件,因此我可以将有效管理数据的内存中状态的负担推到主机操作系统和我的代码之外。

然后我的所有代码都需要担心的是在写入时序列化附加到文件的操作,并允许任意数量的同时读者在文件中搜索以回答请求。

设计

因为单个数据文件可能超过MappedByteBuffer的2GB限制,所以我希望我的设计必须包含一个抽象层,该抽象层采用写入偏移并将其转换为特定2GB段内的偏移量。 / p>

到目前为止一直很好......

问题

这是我开始挂断的地方,并认为采用不同的设计(下面提出)可能是更好的方法。

从阅读到20左右"内存映射"这里有关于SO的相关问题,似乎mmap调用对于在分配时想要连续的内存运行是敏感的。所以,例如,在32位主机操作系统上,如果我试图mmap 2GB文件,由于内存碎片,我的机会很小,映射将成功,而我应该使用类似一系列128MB映射的东西来拉动整个归档。

当我想到这个设计时,甚至说使用1024MB mmap大小,对于托管一些大型数据库的DBMS,所有数据库都表示1TB文件,我现在有千个的内存映射区域内存和我自己在Windows 7上的测试试图在多GB文件中创建几百mmaps,我没有遇到异常,每次我尝试分配太多时,我实际上让JVM陷入段错误在一个案例中,我的Windows 7机器中的视频被切断并重新初始化了我以前从未见过的操作系统错误弹出窗口。

无论“#34”的论点如何,你们都不会处理大型文件。或者"这是一个人为的例子",事实上,我可以使用那些类型的副作用编写类似的内容,使我的内部警报处于高度警戒状态,并考虑替代impl(下图)。

BESIDES问题,我对内存映射文件的理解是每次文件生成时我都必须重新创建映射,所以对于这个仅在设计中附加的文件,它实际上在不断增长

我可以通过在块中增加文件(一次说8MB)来解决这个问题,并且每8MB重新创建一次映射,但是不断重新创建这些映射的需要让我感到紧张,特别是没有明确的unmap feature supported in Java

问题#1 of 2

鉴于我到目前为止的所有发现,我认为内存映射文件是主要读取重量级解决方案或只读解决方案的良好解决方案,但考虑到需要重新创建不断地映射。

但是随后我看着周围的景观,像MongoDB这样的解决方案在各地都拥有内存映射文件,我觉得我在这里缺少一些核心组件(我知道它在2GB范围内分配时间,所以我想他们正在利用这个逻辑来解决重映射成本问题并帮助维持磁盘上的顺序运行。

此时我不知道问题是Java是否缺少unmap操作,这使得它更加危险并且不适合我的用途,或者如果我的理解不正确并且有人可以指出我北部。

替代设计

上面提出的内存映射的另一种设计,如果我对mmap的理解是正确的,我将使用如下:

定义a direct ByteBuffer合理的可配置大小(大致为2,4,8,16,32,64,128KB),使其与任何主机平台轻松兼容(不必担心DBMS)本身导致颠簸情况)并使用原始FileChannel,一次执行文件1 buffer-capacity-chunk的specific-offset reads,完全放弃内存映射文件。

缺点是现在我的代码必须担心像#34;我从文件中读取的内容是否足以加载完整的记录?"

另一个缺点是,我无法利用操作系统的虚拟内存逻辑,让它保持更多的热点"数据在内存中为我自动;相反,我只是希望操作系统使用的文件缓存逻辑足够大,可以在这里为我做一些有用的事情。

问题#2 of 2

我希望能够确认我对所有这一切的理解。

例如,文件缓存可能很棒,在两种情况下(内存映射或直接读取),主机操作系统将保留尽可能多的热数据,大文件的性能差异可以忽略不计。< / p>

或许我对内存映射文件(连续内存)的敏感要求的理解是不正确的,我可以忽略所有这些。

2 个答案:

答案 0 :(得分:15)

您可能对https://github.com/peter-lawrey/Java-Chronicle

感兴趣

在此我创建了多个内存映射到同一个文件(大小是2到1 GB的功率)文件可以是任何大小(最大硬盘大小)

它还会创建一个索引,以便随机查找任何记录,每条记录可以是任意大小。

它可以在进程之间共享,并用于进程之间的低延迟事件。

如果您想使用大量数据,我假设您使用的是64位操作系统。在这种情况下,MappedByteBuffer列表将是您所需要的。为工作使用正确的工具是有意义的。 ;)

即使数据大小约为主内存大小的10倍(我使用的是快速SSD驱动器,因此YMMV),我发现它的性能也很好。

答案 1 :(得分:2)

我认为你不应该担心mmap'ping文件大小达2GB。

将MongoDB的源代码作为数据库利用内存映射文件的一个示例,您会发现它总是映射MemoryMappedFile::mapWithOptions()中的完整数据文件(调用MemoryMappedFile::map())。数据库跨越多个文件,每个文件的大小最大为2GB。此外,它还预先分配数据文件,因此无需在数据增长时重新映射,这可以防止文件碎片。通常,您可以使用此DB的源代码来激发自己的灵感。

相关问题