23.8.高级主题
23.8.1. 调整
调整可调参数,使 ZFS 在不同的工作负载下表现最佳。FreeBSD 将 ZFS 内核可调参数作为 sysctl(8) 变量暴露在 vfs.zfs 下;zfs(4) 记录了每个可调参数及其默认值。需要在引导时生效的值设置在 /boot/loader.conf 中;运行时可调整的值用 sysctl(8) 调整,并在 /etc/sysctl.conf 中永久保存。
vfs.zfs.arc.min- ARC 的下限大小。默认值0让 ZFS 自动选择一个较小的最小值。增大此值可防止其他应用程序挤出整个 ARC。可在运行时用 sysctl(8) 调整此值,并在 /boot/loader.conf 或 /etc/sysctl.conf 中设置。vfs.zfs.arc.meta_balance- ARC 中缓存元数据与文件数据之间的平衡;值大于100时越来越倾向于元数据,默认值为500。如果工作负载涉及对大量文件和目录的操作,或频繁的元数据操作,增大此值可以提高性能,但代价是较少的文件数据能适应 ARC。此可调参数替代了 OpenZFS 2.2 中移除的vfs.zfs.arc.meta_limit。可随时通过 sysctl(8) 调整此值。vfs.zfs.vdev.min_auto_ashift- 池创建时自动使用的最低ashift(扇区大小)。该值是 2 的幂。默认值9表示2^9 = 512,即 512 字节的扇区大小。为了避免 写放大 并获得最佳性能,应将此值设置为池中设备使用的最大扇区大小。有关 4 KB 扇区驱动器上的池为何应使用ashift值 12 的说明,请参见 创建和销毁存储池。vfs.zfs.prefetch.disable- 禁用预取。值为0时启用,值为1时禁用。默认值为0。预取通过将比请求的块更大的数据块读取到 ARC 中,来期望稍后会需要这些数据。如果工作负载有大量随机读取,禁用预取可能会通过减少不必要的读取来提高性能。可随时通过 sysctl(8) 调整此值。vfs.zfs.txg.timeout- 事务组 之间的最大秒数。当前事务组写入池中,如果自上一个事务组以来经过了此时间,则启动新的事务组。如果写入了足够的数据,事务组可能会提前触发。默认值为 5 秒。较大的值可能会通过延迟异步写入来提高读取性能,但这可能导致写入事务组时性能不均匀。可随时通过 sysctl(8) 调整此值。vfs.zfs.l2arc.write_max- 限制每秒写入 L2ARC 的数据量。此可调参数通过限制写入设备的数据量,延长 SSD 的使用寿命。可随时通过 sysctl(8) 调整此值。vfs.zfs.l2arc.write_boost- 将此可调参数的值添加到vfs.zfs.l2arc.write_max中,并在从 L2ARC 驱逐第一个块之前,提升 SSD 的写入速度。这个“加速预热阶段”减少了重启后空 L2ARC 带来的性能损失。可随时通过 sysctl(8) 调整此值。vfs.zfs.l2arc.rebuild_enabled- 让 L2ARC 的内容在重启后持久存在。默认值1让 ZFS 在导入池时根据缓存设备上存储的日志块重建 L2ARC,避免了空缓存原本需要的漫长预热期。将此值设置为0可始终从空的 L2ARC 开始。vfs.zfs.vdev.max_active- 池中每个设备上活跃 I/O 请求的上限数量。较高的值会保持设备命令队列满,从而可能提高吞吐量。较低的值则会减少延迟。可随时通过 sysctl(8) 调整此值。vfs.zfs.resilver_min_time_ms- 重新同步 在事务组刷新之间工作的最短时间(以毫秒为单位);默认值为3000(3 秒)。当降级池面临丢失另一块设备的风险时,增大此值可更快完成重新同步,但代价是减慢正常的池操作。可随时通过 sysctl(8) 调整此值。
23.8.2. ARC 大小与监控
ZFS 将最近和频繁使用的数据和元数据保存在 RAM 中的 ARC 中。ARC 按需增长直到 vfs.zfs.arc.max,并在其他程序需要内存时再次收缩,因此在专用存储服务器上,大部分 RAM 处于使用状态是正常的。ARC 以块在磁盘上的压缩形式存储它们,并在访问时解压。这种 压缩 ARC 让相同数量的 RAM 能缓存更多数据,提高了有效命中率。
top(1) 在其内存摘要行中显示当前 ARC 大小。kstat.zfs.misc.arcstats sysctl(8) 树暴露了详细的统计信息,包括当前大小、目标最大值以及命中和未命中计数器:
filesystems/zfs-stats Port 以人类可读的报告形式总结这些统计信息。
ARC 在内存压力下让出内存,但在 ZFS 与其他大型内存消费者(如数据库、虚拟机或 Jail)共享 RAM 的系统上,这种竞争可能导致双方性能不均匀。为 ARC 设置上限可为其他工作负载留下可预测的内存量。例如,要将 ARC 限制为 8 GB,将以下行添加到 /boot/loader.conf:
用 sysctl(8) 更改 vfs.zfs.arc.max 会立即生效,不过收缩预热的 ARC 会逐步释放内存。
23.8.3. 同步写入、ZIL 与 SLOG
每个池都有一个 ZIL。使用 fsync(2) 或 O_SYNC 标志请求同步语义的应用程序,在 ZFS 将相应的意图日志记录提交到稳定存储之前,不会收到确认。默认情况下,ZIL 位于池的常规 vdev 上,因此每个同步写入都会与正常的池 I/O 竞争。异步写入绕过 ZIL,并随下一个 事务组 一起写入。
专用的 日志 vdev(通常称为 SLOG,即独立日志)将 ZIL 移到快速设备上:
SLOG 仅对产生大量同步写入的工作负载(如 NFS 服务器和数据库)有帮助。对纯异步工作负载没有影响。
几 GB 的 SLOG 容量就足够了:ZIL 最多保存几秒钟的传入写入(大约一个事务组的量),之后 ZFS 会将数据写入池中的最终位置。选择具有断电保护和低持续写入延迟的 SSD,并对日志设备进行镜像。ZFS 仅在崩溃后才读取 ZIL,因此如果未镜像的 SLOG 丢失同时伴随系统崩溃,会丢失那些已确认但未提交到池的写入。
sync 数据集属性控制同步写入行为:
sync=standard(默认值)将同步写入提交到 ZIL,异步写入随下一个事务组写入。sync=always将每个写入都视为同步写入,以性能换取安全性,仅对那些本应请求同步写入却未这样做的应用程序有帮助。sync=disabled将每个写入都视为异步写入。
警告: 使用
sync=disabled时,ZFS 会在同步写入到达稳定存储之前就确认它们。池本身保持一致,但在崩溃或断电后,应用程序和 NFS 客户端会静默丢失最多几秒钟它们认为已安全写入的数据。不要对数据库、NFS 导出或任何其他重要数据使用sync=disabled。
logbias 属性调整 ZFS 使用日志的方式:logbias=latency(默认值)倾向使用 SLOG 以最小化提交延迟,而 logbias=throughput 将同步数据直接写入主池,将 SLOG 容量保留给对延迟敏感的数据集。详见 zfsprops(7)。
23.8.4. 直接 I/O
注意: 直接 I/O 需要 OpenZFS 2.3 或更高版本,首次出现在 FreeBSD 15.0 中。
direct 数据集属性控制 ZFS 如何处理使用 O_DIRECT 标志发出的请求:
direct=standard(默认值)遵循O_DIRECT:正确对齐的读取和写入绕过 ARC。direct=always将每个正确对齐的读取或写入都视为直接请求。direct=disabled静默忽略O_DIRECT,并通过 ARC 处理每个请求。
直接 I/O 避免了 ARC 的内存复制和缓存开销。在由非常快的 NVMe 设备组成的池上,这种开销可能在 I/O 本身的成本中占主导地位,而维护自己缓存的应用程序(如数据库)会因为两次缓存相同数据而浪费 RAM。对于此类工作负载,直接 I/O 可以提高吞吐量并减少内存使用。
ZFS 仍然验证直接读取和写入的校验和,因此数据完整性保护不变。直接写入必须与数据集的 recordsize 对齐;ZFS 会静默地将请求中未对齐的部分重定向到 ARC。完整的对齐规则和限制详见 zfsprops(7)。
23.8.5. recordsize 与 volblocksize
recordsize 属性设置 ZFS 在数据集中存储文件所使用的最大块,默认为 128 KB。将块大小与工作负载的 I/O 模式匹配,可避免读取和写入超出应用程序请求的数据量。
对于执行小随机读取和写入的数据库,在创建数据库文件之前,将 recordsize 设置为数据库页面大小,通常为 8 KB 或 16 KB。对于存储大型媒体文件或备份流(顺序读取和写入)的数据集,recordsize=1M 可减少元数据开销并提高压缩率。卷使用 volblocksize 属性代替,默认为 16 KB,并且在创建卷后无法更改。
更改 recordsize 仅影响新写入的文件;现有文件保留其原始块大小。即使 zfs rewrite 也不会将新的 recordsize 应用于现有文件,因为影响逻辑块大小的属性更改对重写的块没有影响;需要重新复制文件才能用新值存储它们。
最后更新于