zfsとGB単位の巨大ファイル

結論から先に。SQLite3データベースファイルなど、数GB単位のファイルに seek/read/write を頻繁に繰り返してスナップショットをしっかり取りたい用途なら ZFS volume 上に UFS を構築するのがよろし。

zfsのパフォーマンス

zfsは速いと言う人と遅いと言う人がいる。どちらも正解だと思う。 メモリ潤沢マシンで、ソースプログラムとかの細かい ファイルの詰まった tar.gz ファイルなんかを展開するときは超快適だし、 メモリを使い切ってしまったあとなど遅い。

SQLite3にデータを蓄積するSNSを運用していて、既に5年目の現在 データファイルが6GBに達した。3GBを超えたあたりから読み込みの 時間が気になるようになり、SSDを足してzfsのキャッシュ(L2ARC)にあてがって 許容できるレベルを保てている。と思っていた。 がしかし、リブート後のメモリキャッシュが 効いていない状態ではL2ARCがあっても壊滅的に遅い。

-rw-r-----  1 s4  wheel  6236690432 Oct 26 10:41 db.sq3

こんな感じのファイルを sum コマンドで全域舐める時間を計った(実運用機 なのでリブートは気軽に試せず数値は超アバウト)。

種別メモリキャッシュありなし
zfs on HDD1分半4分〜10分
zfs on SSD30秒20秒
ufs on HDD8秒1分10秒

サーバの負荷によって値がグルグル変わるので全くもって信頼できない 値だが、zfs on HDDがどんなに頑張ってもufsに勝てないのは間違いない。

ちなみにsumコマンドで読ませているときにtopでプロセス状態を見ると 大概こんな感じ。

28974 s4            1  22    0    10M  2312K zio->i   4   0:06   2.84% sum

zio->i でピタっと止まり、全然CPUが働けてない。

UFSにしよう

さて、UFSの方が巨大ファイルのシーク+IOが速いことが分かったので それにしたい。だが、zfsでスナップショットをとりまくってデータロスへの 安全弁をたくさん作っているのでUFSだとちと面倒。UFSでも mksnap_ffs(8) でスナップショットを作れるのだが、これ、20世代までしか作れないのが落し穴。

どしたらええの?

新たにUFSデバイスなんか追加できないのでzvolでufsを作ってみたところ これが意外に行けそう。

zfs create -V 32G zpool/ufs0
newfs -U zvol/zpool/ufs0
mount -t ufs zvol/zpool/ufs0 /ufshome

snapshotはどうやって取ろうか?

mksnap_ffsで取ったスナップショットを世代管理してrsync --link-dest でリモートに世代バックアップするスクリプトは作ってあるので、それでやるか もしくは、zfs snapshot で zfs volume をバックアップするか、 いずれにしても、zfsそのもののファイルシステムでのスナップショットより 手間は掛かりそうだ。