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

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

情况

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

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

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

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

首先,思考

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

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

设计

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

到现在为止还挺好…

问题

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

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

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

无论“你永远不会处理那么大的文件”或“这是一个人为的例子”的论点,我可以用这些类型的副作用编写代码的事实使我的内部警报处于高度警戒状态。考虑替代impl(下面)。

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

我可以在一定程度上通过以块的forms增加文件(一次说8MB)并且每8MB重新创建映射来解决这个问题,但是不断重新创建这些映射的需要让我感到紧张,尤其是没有明确的unmapfunctionJava支持 。

问题#1 of 2

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

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

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

替代设计

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

定义一个合理可配置大小的直接ByteBuffer (粗略地为2,4,8,16,32,64,128KB),使其可以轻松地与任何主机平台兼容(不需要担心DBMS本身导致颠簸情况)和使用原始的FileChannel,一次执行文件1 buffer-capacity-chunk的特定偏移读取 ,完全放弃内存映射文件。

缺点是现在我的代码不得不担心“我从文件中读取的内容是否足以加载完整的记录?”

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

问题#2 of 2

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

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

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

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

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

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

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

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

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

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

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