后台是开发DBMS内核,特别是数据库检查点处理。游戏规则是这样的,我们需要等待文件上未完成的异步 IO 完成,然后再发出 fsync((。
我们部署的当前解决方案是手动计算正在进行的异步 IO,等待此计数变为 0,然后再同步或 FlushFileBuffer-ing。问题是我们是否真的必须这样做,也许内核/文件系统自己做?
有问题的操作系统主要是Windows和Linux,尽管我也很好奇基于BSD的操作系统如何处理这个问题。
在Linux上,我们使用libaio进行异步IO。
在 Windows 上:是的,对于给定的HANDLE
实例,在执行FlushFileBuffers()
之前,当前的异步 I/O 队列会被清空。如果你正在编写一个数据库,你真的应该使用NtFlushBuffersFileEx()
,它提供了更精细的同步粒度,产生了巨大的差异。
关于 FreeBSD: 当然使用 ZFS, 是的。我不能说我已经测试过 UFS,但如果它不一样,我会感到惊讶。FreeBSD 在任何情况下都将缓存的异步 I/O 实现为内核线程池, 只有未缓存的异步 I/O 才是真正的异步。
在Mac OS上:不知道,更糟糕的是,磁盘I/O语义在最近几个版本中到处都是。它曾经非常好,就像BSD一样,但最近它一直在走下坡路。在任何情况下,异步文件 I/O 在 Mac OS 上几乎都无法使用,最大 16 个深度队列限制加上使用信号完成异步 I/O 的要求很难与线程代码很好地混合。
在 Linux 上:对于同步 I/O,是的,fsync()
强制执行每个 inode 的总排序,如果您的文件系统保证这一点(所有流行的文件系统都这样做(。对于在任何情况下都只适用于O_DIRECT
I/O 的 libaio,我相信块存储层在告诉设备隔离之前会刷新所有排队的 I/O,除非您禁用了屏障。对于io_uring(您应该使用而不是libaio(,对于非O_DIRECT
I/O,排序是文件系统在处理完提交后对每个inode I/O强制执行的任何排序io_uring。对于具有O_DIRECT
I/O 的io_uring,块存储层是一个单例,一旦io_uring处理了提交,就应该在整个系统中强制排序。
我一直提到"一旦io_uring处理了提交",因为io_uring适用于环形缓冲队列。如果将条目添加到提交队列中,它将按io_uring的提交顺序进行处理(即队列被清空(。从提交到io_uring消费提交,没有排序。但是一旦io_uring使用了提交,目标文件系统就会被告知 I/O,并且它实现的任何排序保证都将应用于它发回io_uring的完成排序。因此,使用 io_uring 时,在 I/O 提交后不要继续,直到io_uring从提交队列中耗尽 I/O 提交请求。使用系统调用告诉io_uring清空队列,或者对于轮询清空,您可以看到"最后一个清空项"在内核消耗提交项时以原子方式偏移
它。来源:我是WG21 C++低级I/O标准化参考库的作者。 警告:以上所有内容纯粹来自我的记忆和经验,可能是有点烂或错误。