EXT4 vs XFS vs Btrfs vs ZFSをfioでベンチマークを取ってみました。

#概要
CentOS7のデフォルトのファイルシステムがXFSとなりました。
mkfsコマンドでも、minix, xfs, btrfsが使えるようになりました。
そこで気になるファイルシステムを色々調べ、ベンチマークを自分なり取ってみました。
多少なりともご参考になればと思います。

#色々なファイルシステム
こちらをご参考ください。
http://qiita.com/sion_cojp/items/c8e015db39ddbf43012e

#それぞれファイルシステムを作ってみる
- 今回の環境は
    - CentOS6（ホスト）
        - 4Core, MEM:32G, HDD:300G
    - CentOS7（ゲスト。こちらで計測しております。）
        - vCPU *1, MEM:4G, HDD:40G
- 容量が少なかったため、btrfsのベンチマークが終わった後、zfsにファイルシステムを変更し検証をしております。

~~~bash
### zfsのインストール
$ sudo yum localinstall --nogpgcheck http://download.fedoraproject.org/pub/epel/beta/7/x86_64/epel-release-7-0.2.noarch.rpm
$ sudo yum localinstall --nogpgcheck http://archive.zfsonlinux.org/epel/zfs-release.el7.noarch.rpm
$ sudo yum install kernel-devel zfs
 
# spl zfsをカーネルモジュールにビルド
$ dkms status
spl, 0.6.3: added
zfs, 0.6.3: added
$ dkms install -m spl -v 0.6.3
Error! echo
Your kernel headers for kernel 3.10.0-123.el7.x86_64 cannot be found at
/lib/modules/3.10.0-123.el7.x86_64/build or /lib/modules/3.10.0-123.el7.x86_64/source.

# バージョンが違うと言われたので、入れ変えましょう
$ rpm -qa | grep kernel-devel
kernel-devel-3.10.0-123.6.3.el7.x86_64
$ rpm -e --nodeps kernel-devel-3.10.0-123.6.3.el7.x86_64
$ wget ftp://mirror.switch.ch/pool/4/mirror/scientificlinux/7rolling/x86_64/os/Packages/kernel-devel-3.10.0-123.el7.x86_64.rpm
$ rpm -ivh kernel-devel-3.10.0-123.el7.x86_64.rpm
 
# 再度ビルド
$ dkms install -m spl -v 0.6.3
DKMS: install completed.
$ dkms install -m zfs -v 0.6.3
DKMS: install completed.

# うまくいきました。念のため確認しましょう。
$dkms status
spl, 0.6.3, 3.10.0-123.el7.x86_64, x86_64: installed
zfs, 0.6.3, 3.10.0-123.el7.x86_64, x86_64: installed
 
### パーティション作成。雑に書いてます。。
$ sudo fdisk /dev/vda
n
e
enter enter
p
 
n
l
enter +5G
p
*3回
 
コマンド (m でヘルプ): p
/dev/vda1   *        2048      411647      204800   83  Linux
/dev/vda2          411648    45475839    22532096   8e  Linux LVM
/dev/vda3        45475840    83886079    19205120    5  Extended
/dev/vda5        45477888    55963647     5242880   83  Linux
/dev/vda6        55965696    66451455     5242880   83  Linux
 
コマンド (m でヘルプ): w
パーティションテーブルは変更されました！

# サーバ再起動
$ reboot
~~~ 
~~~bash
### ext4のファイルシステムを作成します
$ sudo mkfs.ext4 /dev/vda5
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
327680 inodes, 1310720 blocks
65536 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=1342177280
40 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912, 819200, 884736
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
~~~ 

~~~bash
### xfsのファイルシステムを作成します 
$ sudo mkfs.xfs /dev/vda6
meta-data=/dev/vda6              isize=256    agcount=4, agsize=327680 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=0
data     =                       bsize=4096   blocks=1310720, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
log      =internal log           bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
~~~ 
 
~~~bash
### btrfsのファイルシステムを作成します。
$ sudo  mkfs.btrfs /dev/vda7
WARNING! - Btrfs v3.12 IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using
Turning ON incompat feature 'extref': increased hardlink limit per file to 65536
fs created label (null) on /dev/vda7
    nodesize 16384 leafsize 16384 sectorsize 4096 size 5.00GiB
Btrfs v3.12
~~~ 

~~~bash
### （btrfsの検証が終わったら）zfsのファイルシステムを作成します。
# zfsのストレージプール作成
$ zpool create zfs /dev/vda7
$ df | grep zfs
zfs                         5128576       0  5128576    0% /zfs
 
# zfsのファイルシステム作成
$ zfs create zfs/test
$ zfs listcd
NAME       USED  AVAIL  REFER  MOUNTPOINT
zfs        148K  4.89G  32.5K  /zfs
zfs/test    30K  4.89G    30K  /zfs/test
 
### ちなみに削除するときは
$ umount zfs/test
$ zfs destroy zfs/test

$ umount zfs
$ zpool destroy zfs
~~~

~~~bash
### マウントしていきます。
$ mkdir -p {ext4, xfs, brtfs}
$ blkid | grep vda[5-7]
/dev/vda5: UUID="○○○" TYPE="ext4"
/dev/vda6: UUID="×××" TYPE="xfs"
/dev/vda7: UUID="△△△" UUID_SUB="□□□" TYPE="btrfs"
 
 
### ３行追加しましょう
$ sudo vi /etc/fstab
UUID="○○○"     /ext4   ext4    defaults        1 2
UUID="×××"     /xfs    xfs     defaults        1 2
UUID="△△△"     /btrfs  btrfs   defaults        1 2
 
$ df -Tmh | grep "vda[5,6,7]\|ファイル"
ファイルシス            タイプ   サイズ  使用  残り 使用% マウント位置
/dev/vda5               ext4       4.8G   20M  4.6G    1% /ext4
/dev/vda6               xfs        5.0G   33M  5.0G    1% /xfs
/dev/vda7               btrfs      5.0G  320K  4.5G    1% /btrfs
~~~

#1.2Gのファイルをコピー & 削除してみる（timeコマンドを用いて測定）
|  |cp real time|rm real time|
|:-----------:|:------------:|:------------:|
|ext4|0m9.723s (124MB/s)|0m1.905s(631MB/s)|
|xfs|<b>0m7.013s (171MB/s)</b>|<b>0m1.258s(960MB/s)</b>|
|btrfs|0m8.505s (141MB/s)|0m1.329s(863MB/s)|
|zfs|1m40.611s(12MB/s)|0m2.589s(480MB/s)|

- 削除が遅いと言われたxfsだけど、全然速い。
- 基本的なseq/read,writeが圧倒的に早いのだろう。
- zfsはメモリが少ないのかめちゃくちゃ遅い。

#fioでベンチマーク測定する
~~~bash
### fioのインストール
$ sudo yum install -y libaio-devel
$ wget http://pkgs.repoforge.org/fio/fio-2.1.7-1.el7.rf.x86_64.rpm
$ rpm -ivh fio-2.1.7-1.el7.rf.x86_64.rpm
 
### fioコマンドを打つ時は、各ファイルシステムのディレクトリに移動してください。
cd {/ext4, /xfs, /brtfs, /zfs/test}
 
### 実行コマンド。seq,random/read,write を図ります。
### bs=4K
$ fio -rw=read -bs=4k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=seqread
$ fio -rw=write -bs=4k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=seqwrite
$ fio -rw=randread -bs=4k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=randread
$ fio -rw=randwrite -bs=4k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=randwrite
 
### bs=16K
### 実行コマンド。seq,random/read,write を図ります。
$ fio -rw=read -bs=16k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=seqread
$ fio -rw=write -bs=16k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=seqwrite
$ fio -rw=randread -bs=16k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=randread
$ fio -rw=randwrite -bs=16k -size=100m -numjobs=40 -runtime=60 -direct=1 -invalidate=1 -ioengine=libaio -iodepth=32 -iodepth_batch=32 -group_reporting -name=randwrite
~~~

fioの詳細はこちら

|オプション|詳細|
|:------------:|:------------|
|bs|ブロックサイズの指定。<br>今回はlinuxのデフォルトブロックサイズとMySQLを想定して4Kと16K|
|size|この測定で読み書きするファイルサイズ|
|numjobs|ジョブのクローン数。同時にいくつIOを発生させるか。|
|rutime|測定時間（秒）|
|direct|ダイレクト転送。1は有効にする|
|invalidate|バッファキャッシュ。1は無効（デフォルト）|
|ioengine|利用するIOエンジン。liabaioはLinuxカーネル内で実装されてる非同期IO|
|iodepth|queue depthともいう。どれくらい平行して非同期読み書きを行うか。<br>一度に受け取れるコマンドの数。|
|ipdepth_batch|IO数をどれだけ出すか。基本はiodepthと同じ値にする。|
|ipdepth_batch_complete|どれだけのIOをすぐに回収するか。<br>デフォルトは1で、理由はカーネルから回収するプロセスの最小IO=1を求めるから。|
|group_reporting|結果をjob単位でなく、グループ単位で出力|

#測定結果（IOPS）
- 3G(size)のファイル1個（numjobs）でベンチマークしても大差は全然ないので、100Mのファイル40個でいってみます。
- 皆さんが運用してるMySQLのページサイズを設定して測っても良いでしょう
- $ mysql -uroot -e 'show status' | grep page_size

##bs=4K

※単位はIOPSです。

||ext4(4K)|xfs(4K)|btrfs(4K)|zfs(4K)|
|:------------|:------------|:------------|:------------|:------------|
|seq-read|6155|<b>17434</b>|10631|測定不能|
|seq-write|639|<b>1192</b>|759|測定不能||
|random-read|<b>1609</b>|1438|1532|測定不能|
|random-write|188|188|<b>191</b>|測定不能|

##bs=16K

※単位はIOPSです。

||ext4(16K)|xfs(16K)|btrfs(16K)|zfs(16K)|
|:------------|:------------|:------------|:------------|:------------|
|seq-read|<b>6864</b>|6502|3036|測定不能|
|seq-write|<b>1085</b>|1082|462|測定不能||
|random-read|1086|<b>1208</b>|387|測定不能|
|random-write|172|93|82|測定不能|

#fioの結果をみて
- 4KだとIOPSは遅め。ただ、ブロックサイズが変わっても、IOPSがさほど変わらないext4は安定してるといえる。
- xfsは4Kだと爆速。ブロックサイズが大きくなると遅くなっていく傾向でした。
- btrfsもxfsと似ているが、ブロックサイズが大きくなると最悪な結果になってしまいました。
- zfsは遅い。とにかく遅い。メモリが足りなかったのかもしれないが、時間がめっちゃかかりそうだったので測定不能にしました。

# 私なりの総評
<h3>ファイルシステムが混在するのは出来る限り避けたい。<br>
というのをベースにするとXFSが良い。</h3>

- 一般的にlinuxのブロックサイズは4kなので、xfsのほうが良さそう。
<br>

- MySQLでページサイズ大きめならext4でもよい。xfsだとブロックサイズが大きくなるにつれて遅くなってる傾向が見える。
    - ランダムアクセスで劣って入る部分は、あえてフルテーブルスキャンを使ってインデックスを利用しない方法をとってもよい。
- ただ、SSDやらioDriveだと、処理が爆速になるので、そのときはxfsを選ぶのが良いでしょう。
<br>

- ストレージ系にxfsは無謀。（fsck時に1TBにつきメモリ1GBとか論外。）<br>（修正されたという情報もあります。http://xfs.org/index.php/XFS_status_update_for_2010#XFS_status_update_for_January_2010）
- ストレージ系にはZFSやBtrfsを組み込むと良い。将来的におすすめなのはZFSかね。
    - 重複ファイル節約機能や、チェック周りが安心とか嬉しい。今後開発でさらに良くなると思うので、動向を伺いたいですね。
    - しかし現状ではZFSはまだシステムに組み込むのは難しい。内容としては素晴らしい機能を組み込むので開発に期待。