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

Sno*_*all 11 performance zfs hard-drive zfsonlinux

问题

我最近安装了一个新磁盘并在其上创建了一个 zpool:

/# zpool create morez /dev/sdb
Run Code Online (Sandbox Code Playgroud)

使用了一段时间后,我发现它很慢:

/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)
Run Code Online (Sandbox Code Playgroud)

这个测试与我的实际用例非常相似。我正在从磁盘读取中等数量(~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)
Run Code Online (Sandbox Code Playgroud)

和 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)
Run Code Online (Sandbox Code Playgroud)

什么可能导致 ZFS 出现性能问题,我该如何使其更快?

尝试解决方案失败

我还尝试明确设置 zpool 的扇区大小,因为我的磁盘(Seagate ST1000DM003)使用 4096 字节物理扇区:

/# zpool create -o ashift=12 morez /dev/sdb
Run Code Online (Sandbox Code Playgroud)

这并没有提高性能:

/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)
Run Code Online (Sandbox Code Playgroud)

观察

奇怪的是,使用 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)
Run Code Online (Sandbox Code Playgroud)

为什么这只影响 ZFS 文件系统而不影响 zvol?

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)
Run Code Online (Sandbox Code Playgroud)

系统信息

软件

  • Arch Linux,内核版本 4.11.6
  • Linux 0.6.5.10 上的 ZFS
  • 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
Run Code Online (Sandbox Code Playgroud)

sho*_*hok 6

年纪大了,我觉得这个问题值得回答。

fio默认情况下,问题是 4KB 大小的 IOP;相反,ZFS 数据集默认使用 128KB 记录。这种不匹配意味着每次 4K 写入都会导致读取/修改/写入整个 128K 记录。

另一方面,ZVOL 默认使用 8K volblocksize。这意味着 4K 写入会导致 8K 记录的读取/修改/写入周期小得多,幸运的是,两个 4K 写入可以合并为一个 8K 写入(根本不需要读取/修改/写入)。

ZFS 数据集记录大小可以更改zfs set recordize=8K <dataset>,在这种情况下,它应该提供与 ZVOL 或多或少等效的性能。然而,当用于相对较大的传输时(OP 谈到 2 MB 的文件,作为图像,每次访问时都应该完全读取)最好有大的记录大小/卷块大小,有时甚至大于默认设置(128K)。


Ano*_*non 4

注意:由于 fio 作业缺少direct=1http://fio.readthedocs.io/en/latest/fio_doc.html#cmdoption-arg-direct ),因此可能会缓存正在执行的某些 I/O(读取写入)操作系统会扭曲你的结果(并人为地使数字变高)。这本身因以下原因而变得更加复杂:

请注意,O_DIRECT仍然允许执行缓冲 I/O,因为在 Linux 上O_DIRECT更多的是提示(请参阅/sf/answers/3246434061/的参考部分)。

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