我目前正在尝试使用快速 NVMe 设备以不同的方式提高 Debian 上相当大的基于旋转磁盘的软件 raid(mdadm)阵列的写入速度。
我成立使用一对这样的设备(raid1,镜像)来存储文件系统的日志可以带来有趣的性能优势。我为实现这一点而使用的挂载选项是noatime,journal_aync_commit,data=journal
。
在我的测试中,我还发现添加该barrier=0
选项在写入性能方面提供了显著的好处。但是,我不确定此选项在我的特定文件系统配置中是否安全。这是内核文档关于 ext4 写屏障:
写屏障强制日志提交在磁盘上按正确的顺序进行,使易失性磁盘写缓存可以安全使用,但性能会有所下降。如果您的磁盘以某种方式由电池供电,则禁用屏障可能会安全地提高性能。
我使用的特定 NVMe 设备是英特尔 DC P3700它具有内置断电保护功能,这意味着在发生意外关机的情况下,由于储备能量存储,临时缓冲区中仍然存在的任何数据都可以通过储备能量存储安全地提交到 NAND 存储中。
所以我的问题是,如果日志存储在具有电池备份缓存的设备上,而文件系统的其余部分位于没有此功能的磁盘上,我可以安全地禁用 ext4 写入屏障吗?
答案1
我正在写一个新的答案,因为经过进一步的分析,我认为上一个答案是正确的。
如果我们查看该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)以使此设置更安全一些。
答案2
提出问题的另一种方式是:当执行检查点时,即将日志中的数据写入实际文件系统时,ext4 是否会在将事务标记为完成并相应地更新日志之前刷新缓存(在您的情况下是旋转磁盘的缓存)?
如果我们查看 jbd2(负责处理日志)的源代码,检查点.cjbd2_log_do_checkpoint()
我们看到结尾:
__flush_batch(journal, &batch_count);
哪个调用:
write_dirty_buffer(journal->j_chkpt_bhs[i], REQ_SYNC);
所以看起来应该是安全的。
相关:在过去的还提出了一个在日志检查点中使用 WRITE_SYNC 的补丁:原因是写入缓冲区的优先级太低,导致日志在等待写入完成时填满
答案3
如果禁用写屏障可以显著提高性能,则意味着您不应该禁用写屏障,并且您的数据将面临风险。请参阅 XFS FAQ 的这一部分来获取解释。