そんなわけで、FreeBSD機のZFS化をいよいよ決行です。

3TBのHDDを2個つないでミラーリングし、古いHDDからデータを移し始めたんですが、どうにも遅い。10MiB/sぐらいしか出てない感じです。4~5年前のポンコツ機だし、ZFSだと多少は遅くなる覚悟はしてたけど、さすがにこれは遅すぎ。なので一旦コピーは中断して、ちょっと原因を探ってみることにしました。

今回買ったHDDはWestern DigitalのWD30EZRXで、物理セクタは4KiBですが、OSに対しては512Bセクタであるかのように振る舞うタイプ、いわゆるAFT(Advanced Format Technology)を採用したモデルです。

このタイプはパーティション境界が4KiBでないと性能が落ちるので、

# gpart add -t freebsd-zfs '''-a 4K''' -l d1 ada0

のようにgpartの-aオプションでアラインメントは指定していたのですが、これだけでは十分でなかったようです。

試しにddで1000MiBのファイルをゼロで埋めてみると、やはり11KiB/s弱しか出ていません。

# dd if=/dev/zero of=test bs=1m count=1000
1000+0 records in
1000+0 records out
1048576000 bytes transferred in 92.314542 secs (11358730 bytes/sec)

ところで、今回はHDDの一部をGEOM ELIで暗号化していました。そっちはgeli initするときに-s 4096オプションをつけて、セクタサイズを4KiBにしていたので、同じことをやってみると――、

# dd if=/dev/zero of=test bs=1m count=1000
1000+0 records in
1000+0 records out
1048576000 bytes transferred in 33.435481 secs (31361176 bytes/sec)

なんと、3倍の30KiB/s程度出ています。間に暗号化の処理がはさまっているにもかかわらず。ということは、やはり何かセクタサイズに関係があるのは間違いないようです。

ここでハタと、zpoolの認識しているセクタサイズに関するHiroki Satoさんの日記のことを思い出しました。それによると、zdbでashiftを調べれば、zpoolが認識しているセクタサイズが分かるとのこと。

早速zdbでセクタサイズを調べてみると、暗号化していない方は「ashift: 9」(つまり2の9乗=512B)、暗号化している方は「ashift: 12」(つまり2の12乗=4KiB)となっていました。やはりこれか……。

そこで、前述の日記で紹介されている、gnopを使ってセクタサイズを強制的に4KiBにする方法を試してみます。

# gnop create -S 4096 /dev/gpt/d1
# gnop create -S 4096 /dev/gpt/d2
# zpool create tank mirror gpt/d1.nop gpt/d2.nop
# zpool export tank
# gnop destroy /dev/gpt/d1.nop
# gnop destroy /dev/gpt/d2.nop
# zpool import -d /dev/gpt tank

ふたたびzdbで調べると、今度はばっちり「ashift: 12」になりました。

本当に早くなったか試してみると――、

# dd if=/dev/zero of=test bs=1m count=1000
1000+0 records in
1000+0 records out
1048576000 bytes transferred in 16.747891 secs (62609436 bytes/sec)

おお、6倍も早くなったではないですか!

というわけで、AFTのHDDでZFS使うときはセクタサイズに注意、というお話でした。

※バージョンメモ

  • FreeBSD 9.0-RELEASE-p3 amd64