====== ZFSのSpecial vdevを試してみる ====== 階層化ストレージのZFS版ともいえるSpecial vdevとSpecial Allocation Classについて、[[blog:2020:2020-12-08|1年程前に当サイトでも解説]]した。いつか試そうと思いつつ延び延びになっていたが、いよいよ導入の機運が高まってきたので簡単にテストした。 ===== コマンド ===== まずはコマンドの確認から。以下、da0p1をノーマルvdev(プール本体のストレージ)、da6p1をスペシャルvdevとする。 スペシャルvdev付きプールを作る。vdevタイプとして''special''を指定し、その後にスペシャルvdevに割り当てるデバイスを指定する。 # zpool create -O atime=off ztest da0p1 special da6p1 $ zpool status ztest pool: ztest state: ONLINE config: NAME STATE READ WRITE CKSUM ztest ONLINE 0 0 0 da0p1 ONLINE 0 0 0 special da6p1 ONLINE 0 0 0 既存のプールにスペシャルvdevを追加する場合は''zpool add''で同様に指定する。 # zpool add ztest special da6p1 スペシャルvdevの削除。これはL2ARCやslogの削除なんかと一緒。 # sudo zpool remove ztest da6p1 OpenZFS 2.1.4の時点において、RAIDZプールに追加したスペシャルvdevは削除できないので注意!!(トップレベルvdev削除の制限事項)。 スペシャルvdevデバイスの増減(''zpool attach/detach'')は可能だが、いざ''zpool remove''しようとすると''invalid config; all top-level vdevs must have the same sector size and not be raidz.''エラーとなり削除できない。 slogやL2ARCは削除できるのにどうして…… ただし、remove後にスペシャルvdevからノーマルvdevへ、データの退避が行われる。 $ zpool status ztest pool: ztest state: ONLINE remove: Evacuation of /dev/da6p1 in progress since Wed Feb 23 16:50:17 2022 2.77G copied out of 13.5G at 142M/s, 20.54% done, 0h1m to go config: NAME STATE READ WRITE CKSUM ztest ONLINE 0 0 0 da0p1 ONLINE 0 0 0 special da6p1 ONLINE 0 0 0 $ zpool iostat -v ztest capacity operations bandwidth pool alloc free read write read write ---------- ----- ----- ----- ----- ----- ----- ztest 14.4G 9.13T 16 68 215M 220M da0p1 932M 9.09T 0 68 0 220M special - - - - - - da6p1 13.5G 36.0G 16 0 215M 0 ---------- ----- ----- ----- ----- ----- ----- 退避が完了するとプールからデバイスが除去される。 $ zpool status ztest pool: ztest state: ONLINE remove: Removal of vdev 1 copied 13.5G in 0h1m, completed on Wed Feb 23 16:51:31 2022 27.4K memory used for removed device mappings config: NAME STATE READ WRITE CKSUM ztest ONLINE 0 0 0 da0p1 ONLINE 0 0 0 スペシャルvdevは標準でメタデータのみを格納する設定となっている。小ブロックデータの格納を有効にするには、ファイルシステム毎のプロパティ''special_small_blocks''を0以外の2の冪数に設定する。 # zfs set special_small_blocks=64k ztest/R/pictures $ zfs get -r special_small_blocks ztest NAME PROPERTY VALUE SOURCE ztest special_small_blocks 0 default ztest/R special_small_blocks 0 default ztest/R/pictures special_small_blocks 64K local スペシャルvdevが使われてるかどうかは''zpool iostat -v''で確認できる。書き込み中に見てみると、しっかりスペシャルvdevも使われていることがわかる。 capacity operations bandwidth pool alloc free read write read write ---------- ----- ----- ----- ----- ----- ----- ztest 11.7G 9.13T 0 1.36K 0 213M da0p1 11.4G 9.08T 0 215 0 207M special - - - - - - da6p1 277M 49.2G 0 1.15K 0 6.22M ---------- ----- ----- ----- ----- ----- ----- ===== テスト ===== スペシャルvdevの有無、''special_small_blocks''のサイズ、レコードサイズの違いで簡単にテストを行う。 ==== テスト環境 ==== * テストデータ * 72208ファイル、10.3GiB(平均ファイルサイズ:150KiB) * Special vdevの効果が出やすいであろう、大量の小サイズの画像ファイル群 * テストマシン * OS: FreeBSD 13.0-RELEASE-p6 on Proxmox VE 7.1 * CPU: 4 vCPU (Xeon E5-2680v4. オーバーコミットなし) * RAM: 32GB * HDD: * コピー元: 単体 (仮想ディスク50GB/実態はU.2 SSD) * コピー先: 単体 (10TB 7200RPM SATA HDD×1) コピー先のプールのスペシャルvdevの有無によって、以下の項目を測定する。 * コピー元→コピー先へのファイルコピー所要時間(''rsync -aX src dst/'') * コピー先でファイルの全走査にかかる時間(''find dst > /dev/null'') * コピー先でファイルの全読込にかかる時間(''find dst -print0 | xargs -0 cat'') ARCの影響を避けるため、プールは都度作成し、rsync後はマシンを再起動する。その後、ファイル走査→全読込の順で実行する。 ==== テスト結果 ==== 仮想マシン上での実行のため、結果にはノイズが多く含まれていることに注意。 ^ recsize ^ special_small_blocks ^ コピー時間(秒) ^ ファイル走査時間(秒) ^ ファイル読込時間(秒) ^ スペシャルvdev使用量(MiB) ^ 備考 ^ | 128k | - | 284 | 17.8 | 539 | - | Special vdevなし | | ::: | 0 | 281 | 2.5 | 493 | 319 | メタデータのみSpecial vdevを利用 | | ::: | 4k | - | - | - | 320 | sdev容量のみ測定 | | ::: | 8k | - | - | - | 321 | | | ::: | 16k | - | - | - | 322 | | | ::: | 32k | - | - | - | 336 | | | ::: | 64k | 279 | 2.6 | 433 | 684 | | | ::: | 128k | 280 | 2.8 | 203 | 13824 | 全データがSpecial vdevに行く | | 1M | - | 280 | 17.6 | 400 | - | Special vdevなし | | ::: | 0 | 285 | 2.5 | 358 | 34 | | | ::: | 4k | - | - | - | 35 | sdev容量のみ測定 | | ::: | 8k | - | - | - | 36 | | | ::: | 16k | - | - | - | 37 | | | ::: | 32k | - | - | - | 52 | | | ::: | 64k | 274 | 2.7 | 352 | 400 | | | ::: | 128k | 276 | 2.8 | 286 | 3102 | | {{ :blog:2022:zfs_special_vdev_comparison.png |}} ※special_small_blocks=4k~32kは後から測定したため、スペシャルvdevの容量のみ。グラフには加えていない。1MBずつ増えてて本当かよ?と思ったが、再度試しても同じだったので間違ってるわけではなさそう。 スペシャルvdevの有無でファイルコピー(書き込み。赤線)時間に有意な差は見られなかった。ただし、これはコピー元の読み込みで律速してる可能性が否定できない。コピー先の書き込み状況をiostatを眺めてみると間欠動作となっていた。 ファイルの全走査はスペシャルvdevがあると劇的に高速化されるようだ。iostatを見てみると、スペシャルvdevで読み込み処理が走っており、SSD上のメタデータが使わているものと思われる。期待通りの挙動ですな。 ファイルの全読込も、スペシャルvdevに保存されているデータ量に応じて短縮されており、こちらも想定通り。recsize=128kでspecial_small_blocks=128kとすると、全データがスペシャルvdevに保存されるというのも期待通りの結果だった(special_small_blocksで指定されたサイズ**以下**のレコードのデータがスペシャルvdevに保存される仕様。)iostatを見てみると、見事に全てスペシャルvdevに書き込まれていることが分かる。 capacity operations bandwidth pool alloc free read write read write ---------- ----- ----- ----- ----- ----- ----- ztest 13.2G 9.13T 0 2.21K 0 236M da0p1 0 9.09T 0 3 0 15.2K special - - - - - - da6p1 13.2G 36.3G 0 2.20K 0 235M ---------- ----- ----- ----- ----- ----- ----- 少し意外だったのは、''special_small_blocks''が0、すなわちメタデータのみをスペシャルvdevに保存した場合でも、ファイル読込性能が向上したという点。今回は小さな大量のファイルが対象だったため、読込み処理におけるメタデータの処理割合が多かったのが要因だろう。 また、スペシャルvdevと直接は関係ないが、レコードサイズでメタデータサイズが大きく変化するというのは新たな発見だった。全くの推測だが、おそらくレコードごとに生成されるチェックサムの総量が影響しているのだろう。10.3GiBを128kiBレコードで割ると約84000レコードで、それぞれにfletcher4(4バイト)のチェックサムが付くと、合計330MiBとなる。同様に1MiBレコードでは41MiBとなり、これらはメタデータのみをスペシャルvdevに保存した際の容量とおおむね一致する。 さらに1MiBレコードではファイル読込が明らかに速くなっており、[[blog:2022:2022-02-21|圧縮率の観点]]等も考慮すると積極的に128KiB以上のレコードサイズを使っていくのが良さそう。 ''special_small_blocks''の設定をどうするかは悩ましいところ。扱うデータの種類やワークフローはもとより、TXG関連の設定やその時々の負荷量などで、書き込みがスペシャルvdev行きとなるかどうかが変わってくると思われ、見積もるのが難しい。今回は64k/128kの2パターンしか見なかったが、運用においては、より小さな設定値も検討に値するだろう。むしろ、とりあえず4kあたりから始めて様子を見るのがいいのかもしれない。 → 気になったので4k~32kを追試したけど、32k以下は殆ど効果がなさそう。スペシャルvdevの運用としては、容量が見積もりやすいメタデータのみとするか、ある程度余裕を持たせて64kで始めるの2択になるかも。 メタデータだけでもスペシャルvdevの効果は期待できそうなので、SSDに余裕があるならL2ARCよりも優先的に割り当てて良さそうに思う。