以下は、Linux カーネル文書、Documentation/bcache.txt の kanda.motohiro@gmail.com による訳です。原文と同じ、GPL v2 で公開します。実装に興味のある方は、bcache internals を参照。
大きくて遅い raid 6 と、 X-25E がいくつかあったら、キャッシュに使えると良いでしょう。。。なので、 bcache です。
Wiki と git リポジトリは、ここです。
http://bcache.evilpiepirate.org
http://evilpiepirate.org/git/linux-bcache.git
http://evilpiepirate.org/git/bcache-tools.git
bcache は、SSD の性能特性に合わせて設計されています。確保は、消去ブロックの大きさのバケツ単位でのみ行われます。キャッシュエクステント(単一セクターから、バケツ長までの任意の長さです。)を追跡するためには、Bツリーとログのハイブリッドを使います。あらゆる手段を使ってランダムライトをしないように設計されています。消去ブロックはシーケンシャルに使われ、再使用の前に破棄が実行されます。
ライトスルーとライトバックのキャッシュがサポートされます。ライトバックはデフォルトではオフですが、実行時にいつでもオンやオフにできます。bcache は、あなたのデータを守るために十分留意します。クリーンでないシャットダウンに安全に対処します。(実はクリーンシャットダウンという概念はありません。bcache は、ライトはステーブル記憶域に書かれるまで、完了を返しません。)
ライトバックキャッシュは、キャッシュのほとんどを、ライトのバッファに使えます。背後のデバイスにダーティデータを書くのは、常に、シーケンシャルに、インデックスの最初から最後にスキャンして行われます。
ランダム IO こそが、SSD が得意とすることですから、大きなシーケンシャル IO をキャッシュする利点はあまり、一般的にはありません。bcache はシーケンシャル IO を検出して、スキップします。さらに、タスクごとに実行中の IO 長の平均を保持して、それがカットオフ以上であるときは、そのタスクからのすべての IO をスキップします。すべてのシークの最初の 512k をキャッシュすることはしません。このため、バックアップや、大きなファイルのコピーは、完全にキャッシュをバイパスするはずです。
フラッシュのデータ IO エラーが起きたら、bcache はディスクから読んで回復するか、あるいはそのキャッシュエントリを無効化します。回復不能なエラーの場合、(メタデータあるいはダーティデータ)キャッシュは自動的に停止されます。キャッシュにダーティデータがあった場合、まずライトバックキャッシュをやめ、すべてのダーティデータがフラッシュされるのを待ちます。
やってみましょう:
bcache-tools リポジトリの、 make-bcache が必要です。キャッシュデバイスと背後のデバイス(backing device)は、使う前にフォーマットして下さい。
make-bcache -B /dev/sdb
make-bcache -C /dev/sdc
make-bcache は、複数のデバイスを同時にフォーマットできます。キャッシュデバイスと背後のデバイスを同時にフォーマットすれば、手作業のアタッチは不要です。
make-bcache -B /dev/sda /dev/sdb -C /dev/sdc
bcache-tools には、 udev ルールが含まれ、bcache デバイスはカーネルに直ちに認識されます。udev がないときは、以下のように、手でデバイスを登録します。
echo /dev/sdb > /sys/fs/bcache/register
echo /dev/sdc > /sys/fs/bcache/register
背後のデバイスを登録すると、 bcache デバイスが /dev に現れます。これで、通常通りにフォーマットして、使うことできます。しかし、新しい bcache デバイスを最初に使うときは、キャッシュにアタッチするまでは、それはパススルーモードで動作します。アタッチのセクションを参照下さい。
デバイスはこう見えます。
/dev/bcache<N>
ここにも現れます。 (with udev):
/dev/bcache/by-uuid/<uuid>
/dev/bcache/by-label/<label>
使い始めるには、
mkfs.ext4 /dev/bcache0
mount /dev/bcache0 /mnt
bcache デバイスは、 sysfs の /sys/block/bcache<N>/bcache 経由で制御できます。
キャッシュデバイスは、セットで管理されます。セットに複数のキャッシュがある構成はまだサポートされていませんが、将来、メタデータとダーティデータのミラーができるようになるでしょう。キャッシュセットは、 /sys/fs/bcache/<UUID> に現れます。
アタッチ:
キャッシュデバイスと背後のデバイスが登録されたら、背後のデバイスをキャッシュセットにアタッチして、キャッシングを有効にしなければいけません。/sys/fs/bcache に現れる、キャッシュセットの UUID を使って、こうします。
echo <CSET-UUID> > /sys/block/bcache0/bcache/attach
これは1度やればよいです。次回リブートした時は、すべての bcache デバイスを登録すれば済みます。背後のデバイスが、キャッシュのどこかにデータを持っていれば、キャッシュが現れるまで、/dev/bcache<N> デバイスは作成されません。ライトバックキャッシュを有効にしている時には、特に重要です。
ブートした時に、キャッシュデバイスが見えなくて、そのまま戻ってこない時は、背後のデバイスを強制的に動かします。
echo 1 > /sys/block/sdb/bcache/running
(/sys/block/bcache0 でなくて、 /sys/block/sdb (もしくは、あなたのお使いの背後のデバイス)を使わなくてはいけません。なぜなら、 bcache0 はまだ存在しないからです。パーティションを使っているならば、bcache ディレクトリは、 /sys/block/sdb/sdb2/bcache です。)
後にキャッシュセットが現れた時、背後のデバイスは、それを使いますが、すべてのキャッシュされたデータは無効になります。ダーティデータがあったときは、ファイルシステムが回復不可能になることがあります。 ext4 の fsck が奇跡を起こさない限り、大規模なファイルシステム破壊が起きます。
エラー処理:
Bcache は、キャッシュデバイスのライトとリードの IO エラーを透過的に処理して、通常の操作に影響がでないようにします。エラーが多すぎる(しきい値は設定可能で、デフォルトで0です)場合、キャッシュデバイスをシャットダウンして、すべての背後のデバイスをパススルーモードにします。
- キャッシュからのリードがエラーになったら、リトライして、背後のデバイスのリードをします。
- ライトスルーのライトがエラーになったら、キャッシュのその LBA のデータを無効にします。(つまり、キャッシュをバイパスするライトと同じです。)
- ライトバックのライトがエラーになったら、現在はそのエラーをファイルシステムあるいはユーザー空間に戻します。それを、キャッシュをスキップするライトとしてリトライすることもでき、そうすればライトをエラーにしなくても済みますが、これは将来の課題です。
- デタッチのとき、まずすべてのダーティデータをフラッシュします(ライトバックモードのとき)。なお、ダーティデータを読むのに失敗した場合、特に賢いことは何もしません。
性能のトラブルシュート:
Bcache にはたくさんの設定オプションとチューニングパラメータがあります。デフォルトは、典型的なデスクトップとサーバー負荷にとって妥当と思われますが、ベンチマークで最高の成績を出すためのものではありません。
- ライト性能が低い
ライト性能が期待通りでない場合、ライトバックモードでないのかもしれません。それはデフォルトではありません。(成熟していないからではなく、ライトバックモードだと、 SSD に問題があった場合に、データが失われることがあるためです。)
# echo writeback > /sys/block/bcache0/cache_mode
- 性能が低い。あるいは、トラフィックが、SSD に期待通りに行かない
デフォルトで、 bcache はすべてをキャッシュするわけではありません。シーケンシャル IO はスキップされます。ランダム IO をキャッシュするべきだからです。もし10GB のファイルをコピーしたときに、それだけのランダムアクセスされるデータをキャッシュから追い出すのはあなたの望むことではないでしょう。
しかし、キャッシュからのリードをベンチマークするときに、 fio が8GBのテストファイルを書く場合、その振る舞いを変える必要があります。
# echo 0 > /sys/block/bcache0/bcache/sequential_cutoff
デフォルト (4 mb) に戻すには、こうします。
# echo 4M > /sys/block/bcache0/bcache/sequential_cutoff
- トラフィックが、それでもスピンドルに行く。キャッシュミスが起きる。
現実世界で、SSD は常にディスクより速いとは限りません。特に、SSD が遅いとき、多くのディスクが1つの SSD でキャッシュされるとき、あるいは、ほぼシーケンシャルな IO のとき。この場合、SSD がボトルネックとなって、すべてを遅くするのを止めたいことがあるでしょう。
それを防ぐために、 bcache はキャッシュデバイスのレーテンシーを追跡します。レーテンシーがしきい値を超えたら、徐々にトラフィックをしぼります。(シーケンシャルバイパスを使います。)
これを止めるには、しきい値を0にします。
# echo 0 > /sys/fs/bcache/<cache set>/congested_read_threshold_us
# echo 0 > /sys/fs/bcache/<cache set>/congested_write_threshold_us
デフォルトは、リードが 2000 us (2 ミリ秒) 、ライトが 20000 です。
- それでも、同じデータがキャッシュミスする
人々を混乱させる最後の可能性は、古いバグで、キャッシュミスのときのキャッシュ同期にかかわるものです。Bツリーノードが一杯のとき、キャッシュミスは新しいデータのためのキーを入れることができず、データはキャッシュに書かれません。
実際には、これは問題となりません。ライトが来たらすぐに、Bツリーノードのスプリットが起きるので、これが知覚されることはまずないはずです。(特に、 bcache のBツリーノードは巨大で、デバイスの大きな領域をインデックスするためです。)しかし、ベンチマークをしているときに、大量のデータを読んで、キャッシュを暖めようとするときに他のトラフィックがないならば、これが問題となることがあります。
対策: ライトをしてキャッシュを暖めます。あるいは、テストブランチを使って下さい。(この問題の対策がしてあります。)
訳注。以下の sysfs の説明は、大意、のものもあります。
/sys/block/<bdev>/bcache, /sys/block/bcache*/bcache and
(if attached) /sys/fs/bcache/<cset-uuid>/bdev* にあらわれます。
attach
キャッシュセットの UUID を書くと、キャッシュが有効になります。
cache_mode
writethrough, writeback, writearound or none のどれか。
clear_stats
書き込むと、 total stats をゼロにします。(day/hour/5 minute は影響ありません。)
detach
書き込むと、キャッシュセットからデタッチします。ダーティデータがあれば、まず、フラッシュされます。
dirty_data
キャッシュ中の、この背後のデバイスのダーティデータの量。キャッシュセットのそれとは違って、連続的に更新されます。少し、不正確かもしれません。
label
下のデバイスの名前
readahead
先読みの大きさ。デフォルトは0.例えば、1M にすると、キャッシュミスの時のリードをその大きさにまでします。ただし、既存のキャッシュエントリーとオーバーラップしない限り。
running
キャッシュが有効なとき、1。(つまり、/dev/bcache デバイスがあります。パススルーのときも。)
sequential_cutoff
このしきい値を超えると、シーケンシャル IO はキャッシュをバイパスします。最近の 128 IO は追跡されるので、ばらばらに来ても検知します。
sequential_merge
ゼロでない時、最後の 128 要求を覚えていて、以降の要求がシーケンシャルに連続しているかを判定します。
state
背後のデバイスの状態。
no cache: キャッシュセットにアタッチされたことがありません。
clean: キャッシュセットに属します。ダーティデータはありません。
dirty: 自明。
inconsistent: ダーティキャッシュデータがあって、キャッシュセットが見えない時に、ユーザ操作で無理やり背後のデバイスを動かしたため、データがこわれています。
stop
書くと、 bcache デバイスを停止して、背後のデバイスをクローズします。
writeback_delay
クリーンなキャッシュにダーティデータが書かれてから、これだけ秒経つと、ライトバックを始めます。デフォルト30。
writeback_percent
ゼロでない時、ライトバックを抑止して、これだけの割合のダーティデータがたまるようにします。
writeback_rate
writeback_percent がゼロでない時、ライトバックはここで指定するセクター/秒に抑止されます。自動で調整されますが、ユーザーが与えることもできます。
writeback_running
オフだと、ライトバックは全く行われません。ダーティデータはキャッシュがいっぱいになるまで書かれます。ベンチマーク専用。デフォルトはオン。
以下のカウンターが、実行時トータル、1日、1時間、5分、ごとにあります。キャッシュセットディレクトリには、すべての背後のデバイスのカウンターが加算されたものも見えます。
bypassed
キャッシュをバイパスした読み書きの数。
cache_hits
cache_misses
cache_hit_ratio
ヒットとミスは、bcache から見える個々の IO ごとに数えられます。部分的ヒットはミスとします。訳注。ライトはミスしません。
cache_bypass_hits
cache_bypass_misses
バイパスしたものを数えます。
cache_miss_collisions
リードキャッシュミスの後でデータを入れようとしたら、ライトが既にデータを書いていた数。(ふつうはゼロ)
cache_readaheads
先読みがされた回数。
/sys/fs/bcache/<cset-uuid> にあります。
average_key_size
キーあたりの平均データ長
bdev<0..n>
背後のデバイスへのシンボリックリンク
block_size
キャッシュデバイスのブロック長.
btree_cache_size
btree cache が使っているメモリー量。
bucket_size
バケツ長
cache<0..n>
キャッシュデバイスへのシンボリックリンク
cache_available_percent
ダーティデータを持たないため、ライトバックに使うことのできる割合。クリーンなデータに使われないわけではありません。訳注。?
clear_stats
統計をクリアします。
dirty_data
ダーティデータの量。
flash_vol_create
k/M/G 指定でサイズを書き込むと、シンプロビジョニングボリュームが作られます。
io_error_halflife
io_error_limit
これを超えた回数のエラーがあると、キャッシュを無効にします。io_error_halflife は、IO の数で数えた半減期であり、それぞれのエラーの寄与は半減していきます。すべてのエラーの寄与が限界を超えると、ダーティデータが書き込まれ、キャッシュは無効になります。
journal_delay_ms
ジャーナルライトは、ここで指定したミリ秒だけ遅延されます。フラッシュがされれば別です。デフォルト100.
root_usage_percent
ルート btree の使用割合。これが高くなると、ノードスプリットして、木が深くなります。
stop
書くと、キャッシュセットをシャットダウンします。すべてのアタッチされた背後のデバイスがシャットダウンするのを待ちます。
tree_depth
btree の深さ。(単一ノードの btree の深さはゼロ。).
unregister
すべての背後のデバイスをデタッチして、キャッシュデバイスをクローズします。ダーティデータがあれば、ライトバックは無効にされ、フラッシュされるのを待ちます。
このディレクトリには、内部操作のタイミング設定もあります。平均持続時間、平均頻度、最近と最大の持続時間が、それぞれのファイルになっています。ガーベッジコレクション、btree リード、btree ノードソートに、btree スプリット。
active_journal_entries
インデックスより新しいジャーナルエントリーの数。
btree_nodes
btree の全ノード数。
btree_used_percent
btree の使用割合。
bset_tree_stats
補助的な検索ツリーの統計。
btree_cache_max_chain
btree ノードキャッシュのハッシュテーブルの最長のチェーン。
cache_read_races
データがキャッシュから読まれる間に、バケツが再使用、無効になった回数。データは背後のデバイスから読み直されます。
trigger_gc
書き込むとガーベッジコレクションが動きます。
/sys/block/<cdev>/bcache にあります。
block_size
ライトの最小単位。ハードウエアのセクター長と同じであるべき。
btree_written
btree 書き込み (kilo/mega/giga) バイト総量。
bucket_size
バケツ長。
cache_replacement_policy
lru, fifo or random のどれか。
discard
ブーリアン。オンだと、バケツを再使用する前に、discard/TRIM が発行されます。デフォルトはオフ。SATA TRIM はキューされないコマンドなので、遅いため。
freelist_percent
バケツ総数に対する空きリストの割合。書き込むこともでき、空きリストのバケツ数を増やすこともできます。すると、実行時にキャッシュサイズを人工的に減らすことができます。主に、テスト用。
io_errors
エラーの回数。 io_error_halflife で半減します。
metadata_written
データでない書き込み数。(btree 他のメタデータ書き込み。)
nbuckets
このキャッシュのバケツ総数
priority_stats
キャッシュデータのアクセス統計。ワーキングセットサイズを知ることができます。Unused は、データのないもの、 Metadata はメタデータオーバーヘッド、Average はバケツの平均優先度、 Next はクオンタイルのリストで、優先度のしきい値付き。
written
キャッシュに書かれたすべてのデータ。 btree_written と比較すると、bcache のライト具合がわかります。
以上。