一行背景:我是Redis, a NoSQL database的开发者.我正在实现的新功能之一是虚拟内存,因为 Redis 将所有数据都放在内存中。由于 VM Redis 能够将很少使用的对象从内存传输到磁盘,有很多原因可以解释为什么这比让操作系统为我们完成交换工作要好得多(redis 对象由许多分配在非连续区域中的小对象构建地方,当由 Redis 序列化到磁盘时,与它们所在的内存页相比,它们占用的空间少 10 倍,等等)。
现在我有了一个在 Linux 上运行完美的 alpha 实现,但在 Mac OS X Snow Leopard 上运行得不太好。有时,当 Redis 尝试将页面从内存移动到磁盘时,redis 进程会进入不间断等待状态数分钟。我无法对此进行调试,但这发生在对 fseeko()
或 fwrite()
的调用中。几分钟后,调用终于返回,redis 继续工作,没有任何问题:没有崩溃。
传输的数据量非常很小,大约 256 字节。因此,这应该不是执行大量 I/O 的问题。
但是有一个关于作为写入操作目标的交换文件的有趣细节。这是一个大文件(26 GB),创建时使用 fopen()
打开文件,然后使用 ftruncate()
放大。最后,该文件被unlink()
编辑,以便 Redis 继续引用它,但我们确信当 Redis 进程退出时,操作系统将真正释放交换文件。
好的,就这些了,但我想了解更多详细信息。顺便说一句,您甚至可以在 Redis git 中找到实际代码,但考虑到这是一个相当复杂的系统,在五分钟内理解它并非易事。
非常感谢您的帮助。
最佳答案
据我了解,HFS+ 对稀疏文件的支持非常差。因此,您的写入可能正在触发文件扩展,该文件扩展正在初始化/具体化文件的大部分内容。
例如,我知道使用 HFS+ 映射一个新的大空文件,然后在几个随机位置写入会在磁盘上生成一个非常大的文件。这很烦人,因为 mmap 和稀疏文件是处理数据的一种极其方便的方式,而且几乎所有其他平台/文件系统都可以优雅地处理这种情况。
交换文件是线性写入的吗?意思是我们要么替换现有 block ,要么在末尾写入一个新 block 并增加一个可用空间指针?如果是这样,也许更频繁地调用较小的 ftruncate 来扩展文件会导致更短的暂停。
顺便说一句,我很好奇为什么 redis VM 不使用 mmap,然后只是移动 block 以试图将热 block 集中到热页面中。
关于在 Mac OS X Snow Leopard 上执行磁盘 I/O 时,C 程序卡在不间断等待状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2017283/