问题
我最近安装了一个新磁盘并在其上创建了一个 zpool:
/# zpool create morez /dev/sdb
使用一段时间后,我发现它很慢:
/morez# fio --name rw --rw rw --size 10G
read: IOPS=19.6k, BW=76.6MiB/s (80.3MB/s)(5120MiB/66834msec)
write: IOPS=19.6k, BW=76.6MiB/s (80.3MB/s)(5120MiB/66834msec)
此测试与我的实际用例非常相似。我从磁盘读取了中等数量(约 10k)的图像(每个约 2 MiB)。它们是在磁盘几乎为空时一次性写入的,因此我认为它们不会有碎片。
为了进行比较,我测试了 ext4:
/# gdisk /dev/sdb
...
/# mkfs.ext4 -f /dev/sdb1 && mount /dev/sdb1 /mnt && cd /mnt
/mnt# fio --name rw --rw rw --size 10G
read: IOPS=48.3k, BW=189MiB/s (198MB/s)(5120MiB/27135msec)
write: IOPS=48.3k, BW=189MiB/s (198MB/s)(5120MiB/27135msec)
还有 btrfs:
/# mkfs.btrfs -f /dev/sdb1 && mount /dev/sdb1 /mnt && cd /mnt
/mnt# fio --name rw --rw rw --size 10G
read: IOPS=51.3k, BW=201MiB/s (210MB/s)(5120MiB/25528msec)
write: IOPS=51.3k, BW=201MiB/s (210MB/s)(5120MiB/25528msec)
什么可能导致 ZFS 的性能问题以及如何使其更快?
解决方案尝试失败
我还尝试明确设置 zpool 的扇区大小,因为我的磁盘(希捷 ST1000DM003)使用4096字节物理扇区:
/# zpool create -o ashift=12 morez /dev/sdb
这并没有提高性能:
/morez# fio --name rw --rw rw --size 10G
read: IOPS=21.3k, BW=83.2MiB/s (87.2MB/s)(5120MiB/61573msec)
write: IOPS=21.3k, BW=83.2MiB/s (87.2MB/s)(5120MiB/61573msec)
观察
奇怪的是,使用 zvol 的性能非常好:
/# zfs create -V 20G morez/vol
/# fio --name rw --filename /dev/zvol/morez/vol --rw rw --size 10G
read: IOPS=52.7k, BW=206MiB/s (216MB/s)(5120MiB/24852msec)
write: IOPS=52.7k, BW=206MiB/s (216MB/s)(5120MiB/24852msec)
为什么这只会影响 ZFS 文件系统而不影响 zvols?
对 btrfs 进行扩展测试
在评论中,有人认为差异可能是由于缓存造成的。经过进一步测试,我不认为情况如此。我将 btrfs 测试的大小增加到远高于我的计算机内存量,其性能仍然明显高于 ZFS:
/# mkfs.btrfs -f /dev/sdb1 && mount /dev/sdb1 /mnt && cd /mnt
/mnt# $ fio --name rw --rw rw --size 500G --runtime 3600 --time_based --ramp_time 900
read: IOPS=41.9k, BW=164MiB/s (172MB/s)(576GiB/3600003msec)
write: IOPS=41.9k, BW=164MiB/s (172MB/s)(576GiB/3600003msec)
系统信息
软件
- Arch Linux,内核版本 4.11.6
- Linux 0.6.5.10 上的 ZFS
- fio 2.21
硬件
- 正在测试的驱动器:希捷 ST1000DM003,连接到 6Gb/s SATA 端口
- 母板:技嘉X99-SLI
- 内存:8 GiB
ZFS 信息
以下是运行 fio 之前的 ZFS 属性。这些只是使用默认设置创建 zpool 的结果。
# zpool get all morez
NAME PROPERTY VALUE SOURCE
morez size 928G -
morez capacity 0% -
morez altroot - default
morez health ONLINE -
morez guid [removed] default
morez version - default
morez bootfs - default
morez delegation on default
morez autoreplace off default
morez cachefile - default
morez failmode wait default
morez listsnapshots off default
morez autoexpand off default
morez dedupditto 0 default
morez dedupratio 1.00x -
morez free 928G -
morez allocated 276K -
morez readonly off -
morez ashift 0 default
morez comment - default
morez expandsize - -
morez freeing 0 default
morez fragmentation 0% -
morez leaked 0 default
morez feature@async_destroy enabled local
morez feature@empty_bpobj enabled local
morez feature@lz4_compress active local
morez feature@spacemap_histogram active local
morez feature@enabled_txg active local
morez feature@hole_birth active local
morez feature@extensible_dataset enabled local
morez feature@embedded_data active local
morez feature@bookmarks enabled local
morez feature@filesystem_limits enabled local
morez feature@large_blocks enabled local
# zfs get all morez
NAME PROPERTY VALUE SOURCE
morez type filesystem -
morez creation Thu Jun 29 19:34 2017 -
morez used 240K -
morez available 899G -
morez referenced 96K -
morez compressratio 1.00x -
morez mounted yes -
morez quota none default
morez reservation none default
morez recordsize 128K default
morez mountpoint /morez default
morez sharenfs off default
morez checksum on default
morez compression off default
morez atime on default
morez devices on default
morez exec on default
morez setuid on default
morez readonly off default
morez zoned off default
morez snapdir hidden default
morez aclinherit restricted default
morez canmount on default
morez xattr on default
morez copies 1 default
morez version 5 -
morez utf8only off -
morez normalization none -
morez casesensitivity sensitive -
morez vscan off default
morez nbmand off default
morez sharesmb off default
morez refquota none default
morez refreservation none default
morez primarycache all default
morez secondarycache all default
morez usedbysnapshots 0 -
morez usedbydataset 96K -
morez usedbychildren 144K -
morez usedbyrefreservation 0 -
morez logbias latency default
morez dedup off default
morez mlslabel none default
morez sync standard default
morez refcompressratio 1.00x -
morez written 96K -
morez logicalused 72.5K -
morez logicalreferenced 40K -
morez filesystem_limit none default
morez snapshot_limit none default
morez filesystem_count none default
morez snapshot_count none default
morez snapdev hidden default
morez acltype off default
morez context none default
morez fscontext none default
morez defcontext none default
morez rootcontext none default
morez relatime off default
morez redundant_metadata all default
morez overlay off default
答案1
尽管已经过去很久了,但我觉得这个问题值得回答。
fio
默认情况下,问题为 4KB 大小的 IOP;而 ZFS 数据集默认使用 128KB 记录大小。这种不匹配意味着每次 4K 写入都会导致对整个 128K 记录进行读取/修改/写入。
另一方面,ZVOL 默认使用 8K 卷块大小。这意味着 4K 写入会导致 8K 记录的读取/修改/写入周期大大缩短,如果运气好的话,两个 4K 写入可以合并为一个 8K 写入(这需要不读/修改/写)。
ZFS 数据集记录大小可以使用 进行更改zfs set recordize=8K <dataset>
,在这种情况下,它应该提供与 ZVOL 大致相当的性能。但是,当用于相对较大的传输时(OP 谈到 2 MB 文件,它们是图像,每次访问时都应该完全读取),最好有大的recordsize/volblocksize,有时甚至比默认设置(128K)还要大。
答案2
注意:由于 fio 作业缺少direct=1
(http://fio.readthedocs.io/en/latest/fio_doc.html#cmdoption-arg-direct)正在执行的一定数量的 I/O(包括读取和写入)可能会被操作系统缓存,从而扭曲您的结果(并使数字人为地升高)。这本身因以下原因而变得更加复杂:
- Linux 上的 ZFS 要么不支持
O_DIRECT
(因此打开失败),要么支持,然后它会悄悄地回退到缓冲 I/O(参见https://github.com/zfsonlinux/zfs/commit/a584ef26053065f486d46a7335bea222cb03eeea)。 - 在某些情况下,BTRFS 和 ext4 将恢复
O_DIRECT
使用缓冲 I/O。
请注意,O_DIRECT
仍然允许进行缓冲 I/O,因为在 Linux 上O_DIRECT
更多的是提示(请参阅参考部分https://stackoverflow.com/a/46377629/2732969)。
如果您处于无法正确绕过缓存的情况,那么在足够大的区域进行足够的 I/O 以尽量减少缓存的影响至关重要(当然,除非您实际上想要测试缓存)...