为什么 ZFS 比 ext4 和 btrfs 慢这么多?

为什么 ZFS 比 ext4 和 btrfs 慢这么多?

问题

我最近安装了一个新磁盘并在其上创建了一个 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

硬件

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(包括读取写入)可能会被操作系统缓存,从而扭曲您的结果(并使数字人为地升高)。这本身因以下原因而变得更加复杂:

请注意,O_DIRECT仍然允许进行缓冲 I/O,因为在 Linux 上O_DIRECT更多的是提示(请参阅参考部分https://stackoverflow.com/a/46377629/2732969)。

如果您处于无法正确绕过缓存的情况,那么在足够大的区域进行足够的 I/O 以尽量减少缓存的影响至关重要(当然,除非您实际上想要测试缓存)...

相关内容