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中!然后再写入到内存中,等到一个合适的时候(数据量足够或者每5秒同步)写入到对应的磁盘中。实际上,ZIL写入后基本上是不会被读取的,不是抽空再将ZIL上数据移动到硬盘中。只有在系统崩溃、意外断电等情况下,造成ZIL事务未提交的情况下,ZFS才会去检查ZIL,进行事务重放

所以ZFS上面的文档上也说了,ZIL最好用MIRROR,当然只用一个设备来做ZIL也是可以的,不过若是系统崩溃后重启,ZIL设备损坏的话,那一段时间内提交的数据可能无法写入到实际的磁盘中(取决于事务是否完整提交),当然,这并不会影响ZFS本身的文件系统,文件系统还是好的,只不过可能会丢失最近写入的数据

所以对于ZFS来说,有了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设备,所以——问题不大

3.一些奇奇怪怪的问题

之前发现主机重启后,我的两个pool(data(raidz1)、download(mirror))会有几率出现download不会自动导入,需要手动执行zpool import -a才可以导入的情况

经过排查,发现是重启主机后,磁盘对应的sda、sdb号可能会改变,导致zfs按照zpool.cache中的磁盘路径去寻找对应的设备时找不到,所以造成download有几率无法导入

目前采用的解决办法是通过更换导入的设备名来处理,可以参考此文章:Changing disk identifiers in ZFS zpool · PLANTROON’s blog


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

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可能是更好的选择(但是这样共享数据就非常不方便了)