我目前正在尝试使用不同的方法在 Debian 上使用快速 NVMe 设备提高写入速度到相当大的、基于旋转磁盘的软件 raid (mdadm) 阵列。
我发现使用一对这样的设备(raid1,mirrored)来存储文件系统的日志会产生有趣的性能优势。我用来实现这一点的挂载选项是noatime,journal_aync_commit,data=journal
.
在我的测试中,我还发现添加该barrier=0
选项在写入性能方面具有显着优势。但是,我不确定在我的特定文件系统配置中使用此选项是否安全。这是内核文档中关于 ext4 写屏障的内容:
写屏障强制执行日志提交的正确磁盘顺序,使易失性磁盘写缓存可以安全使用,但会降低一些性能。如果您的磁盘以一种或另一种方式由电池供电,则禁用屏障可以安全地提高性能。
我正在使用的特定 NVMe 设备是Intel DC P3700,它具有内置的断电保护,这意味着在意外关机的情况下,由于备用能量,仍然存在于临时缓冲区中的任何数据都会安全地提交到 NAND 存储贮存。
所以我的问题是,如果日志存储在具有电池支持缓存的设备上,而文件系统本身的其余部分位于没有此功能的磁盘上,我是否可以安全地禁用 ext4 写屏障?
我正在写一个新答案,因为经过进一步分析,我认为以前的答案不正确。
如果我们查看该
write_dirty_buffers
函数,它会发出带有REQ_SYNC
标志的写入请求,但不会导致发出缓存刷新或屏障。这是通过blkdev_issue_flush
调用来完成的,该调用通过对标志的验证进行适当的门控,该JDB2_BARRIER
标志本身仅在文件系统安装时启用了屏障时才存在。因此,如果我们回顾一下
checkpoint.c
,障碍仅在事务从日志中删除时才重要。代码中的注释在这里提供了丰富的信息,告诉我们这个写屏障不太可能是必要的,但无论如何作为一种保护措施。我认为这里的假设是,当事务从日志中删除时,数据本身不太可能仍然在驱动器的缓存中徘徊,并且尚未提交到永久存储。但由于这只是一个假设,所以无论如何都会发出写屏障。那么为什么在向主文件系统写入数据时不使用屏障呢?我认为这里的关键是,只要日志是连贯的,文件系统中丢失的元数据(例如,因为它在断电事件中丢失)通常会在日志重放期间恢复,从而避免文件系统损坏。此外,使用
data=journal
还应该保证实际文件系统数据的一致性,因为据我了解,恢复过程还将写出作为其重放机制的一部分提交给日志的数据块。因此,虽然 ext4 实际上并没有在检查点结束时刷新磁盘缓存,但应该采取一些步骤来最大限度地提高断电时的可恢复性:
文件系统应该挂载
data=journal
,而不是data=writeback
(data=ordered
在使用外部日志时不可用)。这一点应该很明显:我们想要日志中所有传入数据块的副本,因为这些数据块很可能在断电事件中丢失。这在性能方面并不昂贵,因为 NVMe 设备非常快。应使用 102400 块的最大日志大小(使用 4K 文件系统块时为 400MB),以最大限度地提高日志重放中可恢复的数据量。这应该不是问题,因为所有 NVMe 设备的大小始终至少有几 GB。
如果在写入密集型操作期间发生意外关闭,问题仍然可能出现。如果事务从日志设备中删除的速度快于数据驱动器自行刷新缓存的速度,则可能会发生不可恢复的数据丢失或文件系统损坏。
因此,在我看来,底线是禁用写屏障并不是 100% 安全的,尽管可以实施一些预防措施(#1 和 #2)以使此设置更安全一些。
提出问题的另一种方法是:在执行检查点时,即将日志中的数据写入实际文件系统时,ext4 是否在将事务标记为已完成之前清除缓存(在您的情况下为旋转磁盘)并且相应地更新期刊?
如果我们查看checkpoint.c中 jbd2(负责处理日志)的源代码,我们会看到最后
jbd2_log_do_checkpoint()
调用:调用:_
所以看起来它应该是安全的。
相关:过去还提出了在日志检查点中使用 WRITE_SYNC 的补丁:原因是写入缓冲区的优先级太低,导致日志在等待写入完成时填满
如果禁用写屏障可以显着提高性能,这意味着您不应该禁用写屏障并且您的数据存在风险。有关解释,请参阅 XFS FAQ 的这一部分。