ZFS调优的一些个人想法

2021年7月28日 0 作者 筱枫

距离上一篇文章已经过去了很长一段时间,最近又在重新研究zfs,发现自己之前对于zfs的理解不够透彻,现在又有了一些新的认识,所以这里打算记录下来

1.读优化

zfs对于读取采用的内存作为缓存(ARC),也可以用ssd作为二级缓存(L2ARC),经过我这么长的一段时间使用下来的感受,其缓存效率是相当高效的

  • 需要添加L2ARC吗?

一般情况下不需要,因为内存足够的情况下,ARC的命中率已经足够,当然你要添加也没有其他问题,不过注意l2arc会占用一部分内存用作索引,可以通过zfs-stats lu来查看

size.value  49299385856   #l2arc存储的数据大小,因为会经过压缩,所以实际上这个值会大于l2arc磁盘容量
hdr_size.value  19266144   #l2arc在内存中的索引大小

对于小内存用户而言,首先推荐自然是加内存,但是如果因为各种原因无法添加内存,则可以适当的添加l2arc用作二级缓存,能够再一定程度上缓解因为内存不够导致的低命中率情况,不过建议最初不要添加太大,可以可以先添加8G的ssd试试,然后根据zfs-stats查看hdr_size、l2arc命中率来进行调整

  • 如何调优ARC?

比较明显的方式是调整zfs_arc_max以及 zfs_arc_min,如果你的电脑主要用做nas,那么这两个值可以适当调高一点,arc_max默认是现有内存一半,arc_min默认则是arc_max的一半,可以根据自己需要进行调整

其他一些比较重要的参数

l2arc_mfuonly:可以设置为l2arc只缓存最常用的数据,不缓存最近使用的数据

l2arc_write_max:默认是8M的写入速度,调高可以更快的写入数据到l2arc中, 在arc缓存数据交换激烈的时候,可以尽量将所有被抛出的数据放入l2arc

  • 我用哪些参数可以来参考目前的arc使用情况?

可以使用zfs-stats工具、arcstat工具、zpool iostat -vl 1等命令来监视arc的使用情况,然后根据自己的需要进行灵活调整

一般而言,用zfs-stats工具就可以得出一个基本的使用情况

zfs-stats u的结果

max_size.value  11274289152         #设置的最大缓存大小
size.value  5551398256              #当前缓存大小
min_size.value  8589934592          #最小缓存大小
target_size.value  9476327608       #目标缓存大小,zfs决定(但我不清楚算法),然后尽可能缓存更多的数据
recently_size.value  3063029881     #最近使用缓存列表大小(MRU)
frequently_size.value  6413297727   #最经常使用缓存列表大小(MFU)
通过对MRU、MFU的观察,可以确定目前情况下系统的负载是偏向于最常使用的数据、还是最近写入的数据

arcstat工具使用说明可以参考:arcstat.1 — OpenZFS documentation

2.写优化

写优化其实很简单,直接使用固态硬盘配置成ZIL即可,不用太大,一两个G就足够了

ZIL实际上就是zfs的写缓存,之前通过搜索引擎找到的一篇文章中,对于ZIL的说法”ZIL不是ZFS的写缓存!“这句话是错误的

对于zfs的写入大体可以分为两种,同步写、异步写

先考虑异步写的情况,对于异步写,ZIL此时仅起到一个维护数据(事务)一致性的作用,数据会被直接写入对应的磁盘,然后在ZIL当中写入标记。异步写ZFS会合并提交,当数据量足够或者每5秒同步一次,用zpool iostat观察的时候,可以看到异步写都是写入对应的数据盘,对于ZIL的写入很少。

其次,考虑同步写的情况,在配置了额外的SLOG设备用作ZIL的时候,则所有同步写的数据都会直接写入ZIL中!然后再在一个合适的时候(这个我并未深入研究)写入到对应的磁盘中,比较这两种写入的区别,我们可以简单的用dd命令测试

dd if=/dev/urandom of=test.file bs=32k count=10240             (异步写)
dd if=/dev/urandom of=test.file bs=32k count=10240 oflag=sync  (同步写)
异步写,注意数据是直接写入到对应的磁盘上
同步写,注意看数据这个时候是写入到ZIL上

但是,对于实际应用来说,大部分应用都是采用的异步写入方式:例如常见的smb,nfs等,但是数据库之类的是采用同步写入的方式。不过,也可以通过对数据集设置sync=disable来让所有数据都异步写入、或者sync=always来让所有数据都同步写入,但是一般而言默认的sync=standard即可满足大部分人的需求

大部分参数而言都不需要进行调整,默认即可。

对于zfs_immediate_write_sz这个参数,文档上的描述:If a pool does not have a log device, data blocks equal to or larger than zfs_immediate_write_sz are treated as if the dataset being written to had the property setting logbias=throughput

Terminology note: logbias=throughput writes the blocks in “indirect mode” to the ZIL where the data is written to the pool and a pointer to the data is written to the ZIL.

换而言之,如果你没有单独使用log设备,那么单次写入数据量小于 zfs_immediate_write_sz  设定的值时,数据会直接写入ZIL,这样速度会有所提升,大于此值的时候,会以间接的形式写入ZIL中,但是对于我们这种额外使用了log设备的情况来说,全部的同步写都会直接写入log设备,所以——问题不大


关于其他的一些调优,可以参考如下文章,本文也是参考了这些文章,并且结合了自己的思考而得出的结论

ZFS 分层架构设计 – Farseerfc的小窝

技术|ZFS 那点事 (linux.cn)

Change zfs_immediate_write_sz behavior (or update man page) · Issue #8530 · openzfs/zfs · GitHub

Tuning of ZFS module – SvennD

Chris’s Wiki :: blog/solaris/ZFSWritesAndZIL (utoronto.ca)

Module Parameters — OpenZFS documentation

题外话:之前用CrystalDiskMark7测试smb的时候,速度很渣,只有3.5k的iops,由于测试数据设置得很小,而且通过arcstat进行监视,看到基本上都打在了缓存上,所以这都是协议的问题,如果想要更好的4k性能,iscsi可能是更好的选择(但是这样共享数据就非常不方便了)