LWN:block write在6.13中支持原子操作!

关注了就能看到更多这么棒的文章哦~

Support for atomic block writes in 6.13

February 20, 2025

This article was contributed by Ritesh Harjani and Ojaswin Mujoo
Gemini-1.5-flash translation
https://lwn.net/Articles/1009298/

原子块写入(Atomic block writes),正如之前 讨论 过几次的那样,是要么完全完成,要么完全不发生的块操作(block operations)。它能确保数据的一致性,并防止部分写入(或称 "torn" 写入)。这意味着磁盘在任何时候,要么包含来自原子写入操作的完整新数据,要么包含来自先前写入的完整旧数据。即使在正在进行的原子写入操作期间发生电源故障,磁盘也绝不会同时包含新旧数据的混合。原子写入引起了许多 Linux 用户,特别是数据库开发者的兴趣,因为此功能可以显著提高性能。

Linux 6.13 的合并窗口(merge window)包含来自 VFS(Virtual File System,虚拟文件系统)维护者 Christian Brauner 题为 "vfs untorn writes" 的拉取请求(pull request),该请求为内核添加了初版的原子写入功能。在本文中,我们将简要介绍这些原子写入是什么,为什么它们在数据库领域很重要,以及 6.13 内核目前支持什么。

为了支持原子写入,需要在 Linux I/O 栈(I/O stack,输入/输出栈)的各个层级进行更改。在 VFS 层,引入了一个接口,允许应用程序请求原子写入 I/O(输入/输出),同时增强了 statx() 以查询原子写入功能。文件系统必须确保物理盘区(physical extent)的分配与底层设备的约束对齐,防止盘区跨越原子写入边界。例如,NVMe(Non-Volatile Memory express,非易失性存储器标准)命名空间(namespace)可以定义原子边界;跨越这些边界的写入将失去原子性保证。

块层(block layer)已更新,以防止拆分原子写入请求的正在进行的 I/O 操作,并将原子写入的设备约束传播到更高层。设备驱动程序也进行了修改,以正确地将原子写入请求排队到硬件。最后,底层磁盘本身必须在硬件级别支持原子写入。NVMe 和 SCSI(Small Computer System Interface,小型计算机系统接口)都提供了此功能,但方式不同。NVMe 隐式支持在指定约束范围内进行操作的原子写入,但 SCSI 需要一个特殊的命令来确保原子性。

为什么数据库关心原子写入?

数据库中的一个常见做法是以固定大小的块执行磁盘 I/O,其中 8KB 和 16KB 是常见的 I/O 大小。但是,数据库还会维护一个日志(journal),该日志记录了足够的信息,以便从可能的写入错误中恢复。其思想是,如果新数据的写入失败,数据库可以将磁盘上存在的旧数据作为起点,并使用日志中的信息来重建新数据。但是,此技术基于以下假设:错误发生后,磁盘上的旧数据仍然一致。如果写入操作已损坏,则该假设可能不成立。

如果 I/O 栈不能保证原子性,则可能会发生损坏。数据库发出的 multi-KB 写入可能会被内核(或硬件)拆分为多个较小的写入操作。这种拆分可能导致写入失败后磁盘上同时存在新旧数据,从而导致不一致的磁盘数据,无法用于恢复。

为了解决这种可能性,数据库采用了一种称为 "double write" 的附加技术。在这种方法中,它们首先将旧数据的副本写入磁盘上的临时存储区域,并确保该操作在写入实际的磁盘表之前成功完成。如果在第二次写入操作中发生错误,数据库可以通过对旧数据的已保存副本执行日志重放来恢复,从而确保准确的数据恢复。但是,正如我们可以猜到的那样,这些 double write 会带来巨大的性能成本,尤其对于写密集型工作负载。这就是数据库追求原子性的原因。如果 I/O 栈可以确保块永远不会被损坏,那么数据库可以安全地禁用 double write,而不会有数据损坏的风险,从而可以恢复丢失的性能。

Linux 中的当前状态

正如在 LSFMM+BPF 2024 期间讨论的那样,一些云供应商可能已经在使用带有 bigalloc 的 ext4 文件系统来宣传原子写入支持。bigalloc 是一项启用基于集群的分配而不是 per-block 分配的功能。这有助于为原子写入操作正确分配对齐的物理块(集群)。但是,在审核代码以说服自己内核不会拆分写入请求后,声明支持原子写入是一回事,而通过定义完善的用户界面正确集成原子写入支持并保证原子性又是另一回事。

在 Linux 6.13 版本中,内核通过直接 I/O(direct I/O)为原子写入提供了一个用户界面。尽管它有一些限制(本文稍后会讨论),但这标志着朝着使数据库开发人员能够探索这些接口迈出了重要一步。

块设备的原子写入能力存储在 struct queue_limits 中。这些限制通过 sysfs 接口在 /sys/block/<device>/queue/atomic_* 处公开给用户空间。文件 atomic_write_unit_min 和 atomic_write_unit_max 指示可以原子写入的最小和最大字节数。如果这些值非零,则表示底层块设备支持原子写入。但是,仅有硬件支持是不够的。如前所述,包括文件系统、块层和 VFS 在内的整个软件栈也必须支持原子写入。

如何使用原子写入功能

目前,原子写入支持仅适用于单个文件系统块。Multi-block 支持正在 开发中,但是这些操作带来了一些更多的约束,社区仍在讨论中。要利用 Linux 6.13 中的当前原子写入功能,必须使用适合应用程序需求的文件系统块大小来格式化文件系统。通常,一个不错的选择是 16KB。

但请注意,ext4 不支持大于系统页面大小的文件系统块大小。因此,在具有 4KB 页面大小的系统(例如 x86)上,ext4 无法使用 16KB 的块大小,因此无法支持该大小的原子写入操作。另一方面,XFS 最近获得了 large block size support,允许它处理大于页面大小的块大小。另请注意,如果系统本身的页面大小为 16KB 或 64KB(例如在 arm64 或 powerpc64 系统上),则 ext4 或 XFS 没有问题,因为这两种文件系统都可以处理小于或等于系统页面大小的块大小。

以下步骤展示了如何使用原子写入功能:

  1. 首先,基于底层块设备支持的原子写入单元创建一个具有合适块大小的文件系统(ext4 或 xfs)。例如:

    mkfs.ext4 -b 16K /dev/sddmkfs.xfs -bsize=16K /dev/sdd
  2. 接下来,使用 statx() 系统调用来确认底层文件系统是否在文件上支持原子写入。与检查块设备 sysfs 路径(仅指示底层磁盘是否支持原子写入)不同,=statx()= 允许应用程序查询是否可以在文件上请求原子写入操作,并确定支持的单元大小,这也确保了整个 I/O 栈都支持原子写入。

    为了方便原子写入,当传递 STATX_WRITE_ATOMIC 标志时, statx() 现在公开以下字段:

    statx() 代码段示例如下所示:

    statx(AT_FDCWD, file_path, 0, STATX_BASIC_STATS | STATX_WRITE_ATOMIC, &stat_buf);printf("Atomic write Min: %d\n", stat_buf.stx_atomic_write_unit_min);printf("Atomic write Max: %d\n", stat_buf.stx_atomic_write_unit_max);
  • stx_atomic_write_unit_min

    : 原子写入请求的最小大小。

  • stx_atomic_write_unit_max

    : 原子写入请求的最大大小。

  • stx_atomic_write_segments_max

    : 段数的上限 — 可以将多个单独的内存缓冲区收集到写入操作中的数量(例如,=IOV_ITER= 的 iovcnt 参数)。目前,这始终设置为 1。

  • 如果支持原子写入,则设置 statx->attributes 中的 STATX_ATTR_WRITE_ATOMIC 标志。

最后,要执行原子写入,请以 O_DIRECT 模式打开文件,并发出设置了 RWF_ATOMIC 标志的 =pwritev2() = 系统调用。确保写入的总长度是介于 atomic_write_unit_min 和 atomic_write_unit_max 之间的 2 的幂,并且写入从文件中相对于写入总长度自然对齐的偏移量开始。

目前,带有 RWF_ATOMIC 的 pwritev2() 仅支持单个 iovec,并且仅限于单个文件系统块写入。这意味着通过 statx() 查询时,文件系统会将最小和最大原子写入单元都报告为单个文件系统块(例如,在上面的示例中为 16KB)。

未来

内核开发人员已经实现了对直接 I/O 原子写入的初始支持,该支持仅限于单个文件系统块。但是,目前正在进行一项工作,旨在将支持扩展到 ext4 和 XFS 文件系统的 multi-block 原子写入。尽管存在局限性,但此功能为那些对 Linux 中的原子写入支持感兴趣的人提供了基础。这也为用户(例如数据库开发人员)提供了一个开始探索和试验此功能的机会。由于该功能仍在积极 讨论 和 开发 中,因此仍然可以与社区合作来增强此功能。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

9acdb0cbc4a4bc358e4708824d051978.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值