如果ZFS清理导致性能下降,你的存储池设计有问题

Hacker News Top 新闻

摘要

本文解释了ZFS清理性能问题实际上是存储池设计余量不足的症状,而非清理本身的问题,并建议通过合理规划大小来避免性能下降。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/07/27 19:46

# 如果清理操作导致性能问题,说明你的 ZFS 设计有问题 - Klara Systems 来源:https://klarasystems.com/articles/if-scrubs-hurt-your-zfs-design-is-broken/ 每个 ZFS 运维人员都经历过这样的对话。每月定期执行的 ZFS 清理(scrub)操作(https://openzfs.github.io/openzfs-docs/man/v2.4/8/zpool-scrub.8.html)正在运行,应用延迟在接下来的两天内翻倍。工作负载变慢,监控告警,用户抱怨。于是你把清理频率改为每季度一次,然后改成“审计之前再做”,最后干脆再也不做了。十二个月后,一块磁盘故障,重新同步(resilver)缓慢地持续了一周,而第二块磁盘上潜伏的校验和错误,将一次常规更换变成了从备份恢复数据的事故。 ## 当维护本身成为故障 从运维的角度看,推迟清理是合理的。清理明显影响了生产,而它带来的价值只有在某天拯救你时才显现出来。但被忽略的是,这种性能痛苦本身在告诉你什么。清理是只读工作负载,运行在 ZFS 最低的 I/O 优先级上。如果调度器中最低优先级的类都能把应用推入不可接受的延迟,说明存储池根本没有性能余量。清理并没有创造这种状况,它只是测量了出来。 如果仅仅读取存储系统中所有数据的行为就能造成足够大的性能影响,以至于降低了数据保护能力,那么这就是煤矿里的金丝雀,在试图拯救你免于即将到来的灾难。 ## 清理实际做了什么 清理会遍历池中所有已分配的块,并根据指向它们的块指针验证其校验和。在健康的池中,它什么也找不到。在不健康的池中,在仍有冗余能力修复它们的时候,它能发现位衰减、故障扇区和错误导向写入——这正是其存在的意义。 自 OpenZFS 0.8(https://openzfs.github.io/openzfs-docs/man/v0.8/index.html)起,清理分两个阶段运行。第一阶段遍历池的元数据——描述数据在磁盘上位置的间接块和 dnode 树——并在内存中构建一个排序后的块范围列表。第二阶段将这些范围作为大的顺序读取发出。相比旧的按块指针顺序的清理(整个运行过程会向旋转磁盘发出大量小随机读取),这是一个重大改进。但第一阶段仍然受限于元数据遍历,而在一个充满小块的碎片化池中,这种遍历是一种随机读取工作负载,排序也无法解决。 清理 I/O 也被明确设置了低优先级。ZIO 调度器限制了每个 VDEV 中正在进行的清理读取数(zfs_vdev_scrub_max_active)和未完成字节数(zfs_scan_vdev_limit)。感受到清理性能痛苦的运维人员通常首先调整这些参数,或者使用 `zpool scrub -p` 在工作时间暂停清理: ``` # zpool status tank scan: scrub in progress since Sat Jul 11 02:00:14 2026 18.4T / 214T scanned at 312M/s, 9.1T / 214T issued at 154M/s 0B repaired, 4.25% done, 16 days 02:11:32 to go ``` 在 214TB 的池上,16 天的估算时间会让人开始讨论“这个月能不能跳过”。调低可调参数会让估算更糟,调高则会让生产更糟。问题不在参数上,而在于池的设计没有为这种工作负载留出空间。 ## 容量规划错在哪里 在清理期间出现问题的池,几乎总是只根据两个数字来规划大小的:可用 TB 数和价格。而维持池健康所需的工作负载从未被纳入电子表格。 第一个错误是容量。池的大小被设计成刚好在第一天容纳数据集,而稳态运行在 85% 到 95% 的满容量成为常态。随着空闲空间的减少,ZFS 的分配行为恶化。分配器更难找到连续区域,空闲空间碎片化,最终 ZFS 不得不使用 gang block,将单个逻辑写入分散到多个碎片上。每一个碎片都是一个块指针,下一次清理必须追踪。一个满的池就是一个碎片化的池,而碎片化的池会将清理的元数据阶段变成数百万次小随机读取。 第二个错误是 IOPS。RAIDZ 能以单位成本提供更多容量,但它每个 VDEV 大致只能提供单块磁盘的随机读取 IOPS,因为 VDEV 中的每块磁盘都参与每个逻辑块的读取。一个 60 盘位的机箱,使用 12T 硬盘,组建为五个 12 宽 RAIDZ2 VDEV,大约提供 500TB 可用容量和约 750 随机读取 IOPS。团队将 500TB 与他们的数据集对比,然后签下采购订单。没人会将 750 IOPS 与生产负载加上并发元数据遍历进行对比——而清理、重新同步或销毁大量快照时,恰恰需要这些 IOPS。 这就是第三个错误:维护从未被分配预算。容量规划只考虑了应用工作负载,然后就此止步。但清理、重新同步、快照清理和发送/接收复制,对于生产池来说并非可选的附加项。它们本身就是生产工作负载,按周期执行,贯穿系统整个生命周期。一个勉强能为应用服务的池,是无法负担自身维护成本的,而每月的清理就是这种赤字被暴露出来的时刻。 ## 解决问题的经济考量 诚实的解决方法是增加更多的 VDEV、使用更窄的 VDEV、改用镜像,以及降低填充率。所有这些都需要花钱,而告诉客户在容量不变的情况下将磁盘数量翻倍,这几乎是一段无法推进的对话。硬件预算是真实存在的约束,忽略这点而提出的设计建议是毫无价值的。 不过,有一个可以利用的不对称性。清理和重新同步的性能问题主要集中在元数据上:小块、分散在池中、按依赖顺序读取。旋转磁盘正好非常不擅长这种负载,但它们依然擅长清理第二阶段中占主导的大顺序读取。真正造成痛苦的负载只占容量的一小部分。将那一部分迁移到为此类工作负载设计的介质上即可。 ## 使用 Special VDEV 卸载元数据 OpenZFS 分配类允许池携带一个 special VDEV,通常是企业级 SSD 或 NVMe 设备组成的宽镜像,用于接收所有池元数据:间接块、dnode、空间映射,甚至如果运行去重,还包括 DDT。数据块则保留在旋转磁盘上。 ``` # zpool add tank special mirror nvme0n1 nvme1n1 nvme2n1 ``` 对清理行为的影响是直接的。元数据遍历阶段——在 HDD 上引发随机读取风暴的部分——现在在 SSD 上以五到六位数的 IOPS 运行,而旋转磁盘只处理它们擅长的顺序数据读取。重新同步的扫描同样受益,所有涉及元数据的生产操作也是如此:目录列表、大量 stat 的工作负载、快照操作和空间统计。 `special_small_blocks` 属性将这种好处扩展到小块文件数据。大小等于或低于阈值的块将随元数据一起存放在 special VDEV 上: ``` # zfs set special_small_blocks=32K tank/vms ``` 容量规划是可衡量的,而非猜测。`zdb -bb tank` 按块类型分解已分配空间;将非数据类别求和,就得到了当前的元数据占用。在典型的 recordsize 为 128K 的池中,元数据远低于已分配空间的 1%。为增长和小块溢出做好规划,一组由 2TB NVMe 设备组成的镜像,就能轻松服务于一个数百 TB 的池。相对于已经在机架上的机箱和硬盘的成本,这仅占硬件预算的个位数百分比,专门用于让维护变得负担得起。 需要注意两点。special VDEV 是一个顶层 VDEV——丢失它就会丢失整个池,所以必须做镜像,并使用三路或四路镜像以匹配池其他部分的容错级别。另外,分配类只适用于新写入。现有元数据会留在旋转磁盘上直到被重写,因此为旧池添加 special VDEV 的效果是逐渐显现的,除非你立即使用 OpenZFS 2.4 中引入的新 `zfs rewrite` 功能。当 special VDEV 填满时,分配会溢出回到主池,而非失败,因此容量规划不足只会降低收益,而不会造成宕机。 ## 清理就是消防演习 每月一次清理是对你最终将要运行的重新同步的预演,两者 I/O 模式相同,只不过重新同步是在池处于降级状态时发生,每过一小时就意味着暴露在二次故障风险中多一小时。一个无法在不影响生产的情况下完成清理的池,根本无法安全地进行重新同步。这就是你设计时必须达到的标准。 因此,将清理的性能痛苦视为它本来的诊断信号。规划池大小时,要为应用及其维护留出空间。保持填充率在分配器能够从容工作的水平。在工作负载真正需要的地方购买 IOPS,并认识到对于清理和重新同步,最需要 IOPS 的工作负载是元数据。一个小的元数据卸载投资是你所能买到的最便宜的余量,它能将清理从一次月度事故变回它本来的设计目标:偶尔拯救你数据的后台噪音。 ## 重回正轨 如果你的系统没有定期执行清理,或者你想调查如何为你的池添加元数据卸载 VDEV,Klara 可以帮助你安全地调整现有池,获取所需的 IOPS。一次关于为满足现有和未来元数据需求而新建元数据 VDEV 的咨询,加上评估 `special_small_blocks` 能带来多大收益,是保护数据的一笔小投资。 Klara 的 ZFS 基础设施支持(https://klarasystems.com/zfs/support/zfs-infrastructure-support/)有助于识别存储瓶颈的根本原因,并提供实用建议来优化池设计、提升性能,并确保诸如清理和重新同步等维护任务不会中断生产。

相似文章

故意破坏ZFS文件

Hacker News Top

一篇技术指南,介绍如何使用'zinject'工具或手动字节级编辑故意破坏ZFS文件,以了解ZFS的错误处理和自愈行为。

你的硬盘可能已经满了

Lobsters Hottest

文章指出,无论硬盘容量多大,往往都会被填满,并以此类比软件优化、技术债务、个人日程安排等领域,建议通过施加人为限制来有效管理资源。