ZFSの重複ファイル節約機能dedupがすげえ(ただしメモリ大尽に限る)

最初にお断りするが、十分なメモリ、あるいはSSDをお持ちでない方は帰っていただいて結構です。


Dedupとは

ZFSにはdedupという機能がある。
その機能を有効にすると、例えばここに1GBのファイルが一つあるとして、それをコピーしても、場所が同じpool内である限り、1GBのディスクしか消費しない。


何それただのハードリンクと思うのは早い。


仮に同一のファイルA, Bがあるとする。いずれも1GB。
ハードリンクすればディスク消費量は1GBだ。
さてここで、ファイルBの一部、1bitだけを変えたいとする。
もはやハードリンクはできず、別個のファイルとして扱うことになる。
したがってディスク消費は2GB。


ではdedupだとどうなるか。
1bit違う1GBのファイル二つがあっても、なんと1GBと128KBだけしかディスクを消費しないという驚きの仕組み。


どういうことかというと、dedupはファイルが同じかどうかをブロックごとに判断するということ。
先の例でいえば、異なる1bitを含むブロック分だけを追加で確保し、それ以外はファイルAと同じものを使う。
なお、ZFSでブロックサイズは512B〜128KB。
つまり大目に見積もっても1GBと128KBだけの消費でよいわけ。


種明かし:テーブルと大量のメモリ

なぜそんなことができるのか。

ZFSはプール上にあるデータのすべてのブロックのハッシュをメモリ上に持っている。
あるファイルをディスクに書き込む前に、ファイルをブロックに分解しハッシュテーブルと照合、書き込み要否を判断するという仕組み。


問題はメモリ上に置くテーブルのサイズだ。
dedupを有効に使うには、テーブルを置けるほど十分なメモリが必要になる。
しかし、のちほど見積もりをするが、数TBのディスクを考えるとメモリで賄うのは少々厳しい。
当然、メモリをこればっかりに使うわけにもいかないのだし。
もちろん、テーブルをハードディスクに置くことはできるが、ファイルを書き込むたびにディスク上のハッシュテーブルを参照することになる。
つまりすっごく遅くなる。
よろしい。ならばSSDだ。

とはいえ、Dedupの目的がディスクの容量節約ならば、安いHDDケチってメモリ、SSD無理して買うのも本末転倒という話になってくる。
月並みだけど、よく考えた方がいいよね。


Dedupの見積もり。

では仮に2TBのデータが詰まったZFSでDedupしたら、どれくらいのメモリが必要か。

ハッシュテーブルは1ブロックにつき320Bytes。

zfsのブロックサイズは512Bから128KBで可変。
ざっくり64KBとみなす。
では計算。

2TB=1024 * 1024 * 1024 * 2 = 2147,483,648KB。
2TB/64KB=33,554,432 blocks

2TBは33,554,432 blocks。
1blockあたり320Bytesなので、かければテーブルに必要なサイズが出る。

33,554,432 blocks * 320 Bytes = 10,737,418,240 = 10GBytes


はい解散。
と言いたくなるレベルですな。
念のため記載するが、上記は2TBのデータを使っているときのdedupテーブルの試算。
2TBのプールに100bytes程度のファイル一つ置いただけなら、ハッシュテーブルは320Bytes程度。


Dedupの活きる道

そもそも2TB全部にDedupするのが間違いであった。
Dedupを活かしたいなら、扱うファイルで決めるのも手。
ざっくり言えばオフィスドキュメント、仮想マシンイメージなど、重複が見込まれるファイルを多く保管するディレクトリに対してのみDedupするとよいでしょう。
一方で動画などはあまり有効ではないでしょう。

なお、プールでDedupするとどれくらい得か、というのは調べることができる。
zdbにプール名を与えてやればいい。
一番下に重複具合が表示される。以下の例では1.06。あまり意味はないってことですな。
ちなみに、あるプールに同一のファイルを3つだけ置くとここが3.00になる。

$ sudo zdb -S vault
パスワード:
Simulated DDT histogram:

bucket              allocated                       referenced
______   ______________________________   ______________________________
refcnt   blocks   LSIZE   PSIZE   DSIZE   blocks   LSIZE   PSIZE   DSIZE
------   ------   -----   -----   -----   ------   -----   -----   -----
     1    8.22M   1.02T   1.02T   1.02T    8.22M   1.02T   1.02T   1.02T
     2     406K   50.6G   50.6G   50.6G     815K    102G    102G    102G
     4      185   17.1M   17.1M   17.1M      835   73.6M   73.6M   73.6M
     8      146   14.2M   14.2M   14.2M    1.75K    182M    182M    182M
    16       17   8.50K   8.50K   8.50K      407    204K    204K    204K
    32      150   16.7M   16.7M   16.7M    5.43K    600M    600M    600M
    64        9    132K    132K    132K      820   9.49M   9.49M   9.49M
   128        6      3K      3K      3K    1.02K    521K    521K    521K
   256        3    129K    129K    129K      884   40.5M   40.5M   40.5M
   64K        1    128K    128K    128K    73.4K   9.18G   9.18G   9.18G
 Total    8.62M   1.07T   1.07T   1.07T    9.10M   1.13T   1.13T   1.13T

dedup = 1.06, compress = 1.00, copies = 1.00, dedup * compress / copies = 1.0

DedupのONのしかた

まあzfs set dedup=ONとかすればいいんだけど、現状使う予定もないので詳しく調べてない。