Skip to content

30.6 ZFS 调优

30.6.1 技术潜能与现实困境

ZFS 的性能优势与高级特性需要针对性的参数调优才能充分发挥,调优策略依赖具体环境,需结合存储硬件、工作负载特征与使用场景作个性化配置,主要调优方向包括 ARC 缓存大小、记录大小、压缩算法选择等。ZFS 不属于典型的开箱即用型文件系统。

30.6.2 调优

调整可调参数,使 ZFS 在不同的工作负载下表现最佳。可以随时通过 sysctl(8) 调整以下值,还可以在 /boot/loader.conf/etc/sysctl.conf 中永久设置某值。

  • vfs.zfs.arc.max - ARC 的最大大小。默认值为 0,对于 FreeBSD,实际取总内存 - 1 GiB 与总内存的 5/8 中的较大值。此值不得低于 64 MiB。如果系统运行其他守护进程或进程可能需要内存,可以使用较小的值。

    注意

    可以使用 sysctl(8) 动态修改 vfs.zfs.arc.max。但是系统运行期间无法再将其改回 0;此外,若将该设定值调得比当前 ARC 实际占用还低,ARC 将不会主动缩减,必须等到系统出现内存压力时才会触发回收。

  • vfs.zfs.arc.min - ARC 的最小大小。默认值为 0,实际取 32 MiB 与总内存的 1/32 中的较大值。可以调整此值,以防止其他应用程序挤出整个 ARC。

  • vfs.zfs.vdev.min_auto_ashift - 池创建时自动使用的最低 ashift(扇区大小)。该值是 2 的幂。默认值为 9,表示 2^9 = 512,即 512 字节的扇区大小。为了避免 写放大 并获得最佳性能,应将此值设置为池中设备使用的最大扇区大小。常见的驱动器有 4 KB 扇区。使用默认的 ashift9 与这些驱动器会导致写放大。在这些设备中,单个 4 KB 写入的数据将以八次 512 字节写入的方式写入。ZFS 尝试在创建池时从所有设备读取原生扇区大小,但具有 4 KB 扇区的驱动器为兼容性考虑报告其扇区为 512 字节。在创建池之前将 vfs.zfs.vdev.min_auto_ashift 设为 122^12 = 4096),即可强制 ZFS 使用 4 KB 块,从而获得最佳性能。

    技巧

    通过 bsdinstall 安装的 FreeBSD ZFS 系统,vfs.zfs.vdev.min_auto_ashift 值默认为 12,参见源代码文件 usr.sbin/bsdinstall/scripts/zfsboot

    强制使用 4 KB 块在计划升级磁盘的池中也非常有用。目前的磁盘使用 4 KB 扇区,而 ashift 值在创建池后无法更改。

    在某些特定情况下,较小的 512 字节块大小可能更为合适。例如,在使用 512 字节磁盘用于数据库或虚拟机存储时,较小的块在小随机读取时会传输更少的数据。这可以在使用较小的 ZFS 记录大小时提供更好的性能。

  • vfs.zfs.prefetch.disable - 禁用预测性预取。值为 0 时启用,值为 1 时禁用。默认值为 0(启用预测性预取)。预取会将比请求的块大得多的数据块读取到 ARC 中,预期稍后会用到这些数据。如果工作负载有大量随机读取,禁用预取可能因减少不必要的读取而提高性能。注意,此参数仅禁用预测性预取,不影响先验性预取(prescient prefetch,如 zfs send 使用的预取),后者永远不会发出最终不被需要的 I/O,因此不会影响性能。

  • vfs.zfs.l2arc.write_max - 限制每个 L2ARC 设备每秒写入的最大数据量。默认值为 67108864 字节(64 MiB)。总 L2ARC 吞吐量随池中缓存设备数量线性增长。

  • vfs.zfs.l2arc.noprefetch - 是否将预取但未被应用程序使用的缓冲区写入 L2ARC。默认值为 1(禁用)。值为 0 时启用。禁用时,预取的数据不会缓存到 L2ARC。将此值设为 0 可将磁盘上的顺序读取缓存到 L2ARC,并在之后从 L2ARC 提供这些读取服务。当 L2ARC 设备在顺序读取上比池磁盘快得多时,这可能是有益的。

  • vfs.zfs.l2arc.mfuonly - 控制从 ARC 缓存到 L2ARC 的内容。默认值为 0,表示 MRU 和 MFU 的数据和元数据都会被缓存到 L2ARC。设为 1 时,仅缓存 MFU 数据和元数据,适用于读写大量不会再次访问的数据时避免浪费 L2ARC 空间的场景。设为 2 时,缓存所有元数据(MRU+MFU)但仅缓存 MFU 数据,适用于希望在数据高周转时尽可能多地缓存元数据的场景。

  • vfs.zfs.l2arc.dwpd_limit - L2ARC 设备的每日写入量(Drive Writes Per Day)限制,以百分比表示,默认值为 100100 等于 1.0 DWPD,即每个 L2ARC 设备每天最多写入自身容量一次。较低的值支持分数 DWPD(50 = 0.5 DWPD,30 = 0.3 DWPD,适用于 QLC SSD)。较高的值允许更多写入(300 = 3.0 DWPD)。实际写入速率始终受 vfs.zfs.l2arc.write_max 限制。值为 0 时禁用 DWPD 速率限制。DWPD 限制仅在初始填充阶段完成且 L2ARC 总容量至少为 arc_c_max 的两倍后才生效。

  • vfs.zfs.txg.timeout - 事务组(transaction group)之间的最大秒数,即脏数据刷写到磁盘的最大间隔。默认值为 5 秒。当前事务组写入池中,如果自上一个事务组以来经过了此时间,则启动新的事务组。如果写入了足够的数据,事务组可能会提前触发。较大的值可能因延迟异步写入而提高读取性能,但这可能导致写入事务组时性能不均匀。

  • vfs.zfs.vdev.scrub_min_active - scrub 操作时每个设备的最小并发 I/O 数。默认值为 1。当 vdev 空闲时,并发数自动提升至 vfs.zfs.vdev.scrub_max_active

  • vfs.zfs.vdev.scrub_max_active - scrub 操作时每个设备的最大并发 I/O 数。默认值为 3。增大此值可加快 scrub 完成速度,但会增加读写延迟并降低吞吐量。

  • vfs.zfs.vdev.rebuild_min_active - sequential rebuild(顺序重建,区别于传统 resilver)操作时每个设备的最小并发 I/O 数。默认值为 1

  • vfs.zfs.vdev.rebuild_max_active - sequential rebuild 操作时每个设备的最大并发 I/O 数。默认值为 3。增大此值可加快 rebuild 完成速度,但会增加读写延迟。

  • vfs.zfs.vdev.nia_delay - 对于非交互式 I/O(scrub、resilver、移除、初始化和重建),并发 I/O 数限制为各队列的 min_active,除非 vdev 处于“空闲”状态。当没有交互式 I/O 活动,且自上次交互式操作以来已完成 vfs.zfs.vdev.nia_delay 个非交互式操作后,vdev 被视为“空闲”,非交互式操作的并发数提升至各队列的 max_active。默认值为 5

  • vfs.zfs.vdev.nia_credit - 某些机械硬盘会以较高优先级处理顺序 I/O,导致并发随机 I/O 延迟达到数秒。为防止非交互式 I/O(如 scrub)独占设备,当存在未完成的交互式 I/O 时,最多只能发送 vfs.zfs.vdev.nia_credit 个非交互式操作。此强制等待确保机械硬盘在合理时间内处理交互式 I/O。默认值为 5

30.6.3 参考文档

与 ZFS 调优相关的文档包括:

  • OpenZFS 项目的官方文档 https://openzfs.github.io/openzfs-docs/index.html,其中包含专门的性能与调优章节,涵盖模块参数、工作负载调优等内容。
  • 《FreeBSD 操作系统设计与实现(第二版)》:包含 ZFS 原理性描述。
  • FreeBSD Mastery: ZFSFreeBSD Mastery: Advanced ZFS:价值有限。
  • Oracle Solaris 管理:ZFS 文件系统:该文档在 OpenZFS 项目启动之前撰写,不包含 OpenZFS 近十五年来的开发进展,仅供参考。