【发布时间】:2012-03-04 21:38:39
【问题描述】:
由于所有上下文,这看起来像是一个很长的问题。下面的小说里面有2个问题。感谢您抽出宝贵时间阅读本文并提供帮助。
情况
我正在开发一个可扩展的数据存储实现,它可以支持在 32 位或 64 位系统上处理大小从几 KB 到 1 TB 或更大的数据文件。
数据存储采用写时复制设计;始终将新数据或修改后的数据附加到数据文件的末尾,并且从不对现有数据进行就地编辑。
系统可以托管1个或多个数据库;每个都由磁盘上的一个文件表示。
实现的细节并不重要;唯一重要的细节是我需要不断地附加到文件并将其从 KB 增长到 MB,再到 GB 到 TB,同时随机跳过文件以进行读取操作以响应客户端请求。
第一想法
乍一看,我知道我想使用内存映射文件,这样我就可以将有效管理内存中数据状态的负担推到主机操作系统上,而不是我的代码。
然后我所有的代码需要担心的是在写入时序列化附加到文件的操作,并允许任意数量的同时阅读器在文件中寻找以回答请求。
设计
由于单个数据文件可能会超过 MappedByteBuffer 的 2GB 限制,我希望我的设计必须包含一个抽象层,该抽象层采用写入偏移量并将其转换为特定 2GB 段内的偏移量。
到目前为止一切都很好......
问题
这就是我开始挂断电话的地方,并认为采用不同的设计(下面提出)可能是更好的方法。
通过阅读关于 SO 的 20 个左右“内存映射”相关问题,看来 mmap 调用对分配时需要连续运行的内存很敏感。因此,例如,在 32 位主机操作系统上,如果我尝试 mmap 一个 2GB 文件,由于内存碎片,我映射成功的机会很小,相反我应该使用类似一系列 128MB 映射的东西来拉整个文件。
当我想到这种设计时,甚至说使用 1024MB 的 mmap 大小,对于托管几个由 1TB 文件表示的大型数据库的 DBMS,我现在有 数千 个内存映射区域内存,在我自己在 Windows 7 上的测试中,我试图在一个多 GB 文件中创建几百个 mmap,我不仅遇到了异常,实际上每次我尝试分配太多时,我都会让 JVM 出现段错误。案例让我的 Windows 7 机器中的视频剪切下来并使用我以前从未见过的操作系统错误弹出窗口重新初始化。
不管“你永远不可能处理那么大的文件”或“这是一个人为的例子”的论点,我可以用这些类型的副作用编写类似的东西,这让我的内心高度警惕-alert 并考虑另一种 impl(如下)。
除了这个问题,我对内存映射文件的理解是,每次文件增长时我都必须重新创建映射,所以对于这个在设计中只附加的文件,它实际上是在不断增长.
我可以在一定程度上解决这个问题,方法是分块增长文件(比如一次 8MB),并且每 8MB 重新创建一次映射,但是需要不断地重新创建这些映射让我很紧张,尤其是没有显式unmap feature supported in Java。
第 1 题(共 2 题)
鉴于到目前为止我的所有发现,我认为内存映射文件是一种很好的解决方案,主要用于读取繁重的解决方案或只读解决方案,但不是写入繁重的解决方案,因为需要重新创建不断映射。
然后我环顾四周,使用诸如 MongoDB 之类的解决方案在所有地方都包含内存映射文件,我觉得我在这里缺少一些核心组件(我确实知道它在 2GB 范围内分配时间,所以我想他们正在使用这种逻辑解决重新映射成本,并帮助维持磁盘上的顺序运行)。
在这一点上,我不知道问题是否是 Java 缺少取消映射操作,这使得它更加危险且不适合我的使用,或者我的理解不正确并且有人可以指出我的北方。
另类设计
如果我对 mmap 的理解正确,我将采用上述内存映射设计的替代设计如下:
将a direct ByteBuffer 定义为合理的可配置大小(大致为 2、4、8、16、32、64、128KB),使其易于与任何主机平台兼容(无需担心 DBMS 本身会导致崩溃的情况) 并使用原始 FileChannel,一次执行文件 1 buffer-capacity-chunk 的specific-offset reads,完全放弃内存映射文件。
缺点是现在我的代码必须担心“我从文件中读取的内容是否足以加载完整记录?”
另一个缺点是我无法使用操作系统的虚拟内存逻辑,让它自动为我在内存中保留更多“热”数据;相反,我只希望操作系统使用的文件缓存逻辑足够大,可以在这里做一些对我有帮助的事情。
第 2 题(共 2 题)
我希望能确认我对这一切的理解。
例如,文件缓存可能很棒,在这两种情况下(内存映射或直接读取),主机操作系统都会尽可能多地保留我的热数据,并且大文件的性能差异可以忽略不计。
或者我对内存映射文件(连续内存)的敏感要求的理解不正确,我可以忽略所有这些。
【问题讨论】:
-
如果您在提出问题后获得了一些见解,请将它们作为答案发布。很多人读过这个问题,他们可以利用这个洞察力。围绕 mmaping 存在大量“无法修复”的错误,例如 bugs.sun.com/view_bug.do?bug_id=6893654(尽管 JVM 段错误和图形驱动程序崩溃更严重!)有趣的是,一个简单、优雅的本机功能如何在托管世界中变得复杂而丑陋。
-
@AleksandrDubinsky 你是对的(关于优雅变得不优雅)——我的最终发现是,如果不给系统引入明显的不稳定性,就无法快速创建 mmap 文件(我不知道是否我在这个线程中澄清了,但我设法蓝屏了我的 Windows 开发机器)。这个细节单独让我想坚持使用 AsyncFileChannel 进行文件 I/O 并避免一起使用 mmap,尽管 Peter(下)在 Chronicle 中取得了重大成功。
-
@AleksandrDubinsky 一旦我能够将虚拟机和我的机器都带到它的膝盖上,明显“误用”映射文件,我就完成了沿着这条路走下去。它们很优雅并且提供了出色的性能,但是从我在 AsyncFileChannel 上所做的更多阅读来看,您似乎可以获得非常接近相同的性能(允许操作系统利用 FS 和磁盘控制器以及 I/O 排序来优化请求)。如果您真的想走 mmap 路径,这里的专家 Peter 是。
标签: java file-io database-design memory-mapped-files bytebuffer