論文翻訳: An Introduction to Bε-trees and Write-Optimization
Michael A. Bender, Martin Farach-Colton, William Jannen, Rob Johnson, Bradley C. Kuszmaul, Donald E. Porter, Jun Yuan, and Yang Zhan
Abstract
Bε-Tree は書き込みに最適化されたデータ構造の一例であり、データベースやファイルシステムなどのアプリケーションのディスク上のストレージを整理するために使用することができる。Bε-Tree は B-Tree に似た Key-Value API を提供するが、特に挿入、範囲クエリー、および Key-Value の更新パフォーマンスが優れている。この記事では Bε-Tree について説明し、B-Tree や Log-Structured Merge Tree (LSM ツリー) との漸近的なパフォーマンスを比較し、実際の性能測定結果を示す。この記事を読み終えた読者は Bε-Tree の仕組み、性能特性、他の Key-Value ストアとの比較、Bε-Tree から最大のパフォーマンスを引き出すアプリケーション設計方法についての基本的な理解が得られるはずである。
Table of Contents
1 Bε-Tree
Bε-Tree は B-Tree [2] とバッファ付きリポジトリツリー [3] の間の漸近的なパフォーマンストレードオフ曲線を示す方法として Brodal と Fagerberg [1] によって提案された。どちらのデータ構造も同じ操作をサポートするが、B-Tree はクエリーを優先し、バッファ付きリポジトリツリーは挿入を優先する。
この記事を含む研究者らは、Bε-Tree がこの曲線の "中間点" を占めるように構成された場合に B-Tree に匹敵するクエリ性能を実現しながら B-Tree よりも桁違いに高速な挿入性能を実現する実用的な有用性があることを認識している。それ以来 Bε-Tree は高性能な商用 TokuDB [4] と BetrFS research file system [5] の両方で使用されている。興味のある読者のために Bε-Tree の簡単なリファレンス実装を作成した。https://github.com/oscarlab/Be-Tree.
まず Bε-Tree に対する基本的な操作がどのように行われるかを説明する。次に、これらの設計上の選択の背景にある動機を示し、これらの選択が漸近解析にどのように影響するかを説明する。
API と基本構造. Bε-Tree は Figure 1 に示すようにディスク上のデータを整理するための B-Tree のような検索ツリーである。B-Tree も Bε-Tree も Key-Value ストアの API をエクスポートする:
B-Tree と同様に、Bε-Tree のノードサイズは基礎となるストレージデバイスのブロックサイズの倍数となるように選択される。一般的な Bε-Tree のノードサイズは数百キロバイトから数メガバイトの範囲である。B-Tree も Bε-Tree も内部ノードはピボットキーと子ポインタを格納し、葉はキーでソートされたキーと値を格納する。説明を簡単にするために、各キーと値またはピボットとポインタのペアを単位サイズと考えるものとするが、実際には B-Tree も Bε-Tree も異なるサイズのキーと値を格納できる。したがって、サイズ
Bε-Tree の特徴は、Figure 1 に示すように内部ノードにもバッファ用のいくらかの空間が割り当てられることである。各内部ノードのバッファは、最終的にこのノードの下のリーフに適用される更新をエンコードするメッセージを格納するために使用される。このバッファはメモリ内のデータ構造ではなくノードの一部であり、ノードがディスクに書き込まれたりメモリから退避されるときには常にそれが行われる。ε の値は 0 から 1 の間でなければならず、内部ノードがピボットに使用する空間量 (
挿入と削除. 挿入は "挿入メッセージ" としてエンコードされ、特定のキーに当ててツリーのルートノードのバッファに追加される。十分なメッセージがノードに追加されてバッファがいっぱいになると、メッセージのバッチがノードの子ノードのいずれかにフラッシュされる。通常、保留中のメッセージが最も多い子ノードが選択される。フラッシュの過程で、各メッセージは最終的に適切な葉ノードに配信され、新しいキーと値が葉に追加される。葉ノードがいっぱいになると B-Tree のように分割される。B-Tree と同様に、内部ノードに子ノードが増えすぎると、そのノードは分割され、バッファ内のメッセージは 2 つの新しいノードに分散される。
バッチでツリーの下にメッセージを移動させることが Bε-Tree の挿入パフォーマンスの鍵となる。Bε-Tree は新しく挿入されたメッセージをルート近くのバッファに格納することで、要素を目的の場所に配置するためにディスク上を探し回ることを回避することができる。Bε-Tree はサブツリーに十分なメッセージが蓄積し I/O コストを償却できる場合にのみメッセージをサブツリーに移動する。これは同じデータを複数回書き換えることになるが、次のセクションでの分析で示すようにより小規模でランダムな挿入のパフォーマンスを向上させることができる。
Bε-Tree はツリーに "墓石メッセージ" を挿入することでアイテムを削除する。これらの墓石メッセージは葉に到達するまでツリーを下ってフラッシュされる。墓石メッセージが葉にフラッシュされると、Bε-Tree は削除されたアイテムと墓石メッセージの両方を破棄する。したがって、削除されたアイテムまたは葉ノード全体は墓石メッセージが葉に到達するまで存在し続けることができる。削除はメッセージとしてエンコードされるため、削除はアルゴリズム的には挿入と非常に似ている。
高性能な Bε-Tree は大量のメッセージがすべて 1 つの葉に向かうケースを検出して最適化する必要がある。1 つの葉を完全に埋め尽くすような一連のキーが挿入されたとする。これらのメッセージを内部ノードに書き込んで、ルートからリーフへの経路上の各ノードにすぐに書き換えるのではなく、これらのメッセージはその葉の他の保留中のメッセージと共に葉にフラッシュすべきである。TokuDB と BetrFS の Bε-Tree 実装には一連のメッセージがすべて 1 つの子に送られる場合に中間ノードへの書き込みを回避するためのヒューリスチックがいくつか含まれている。
ポイントクエリーと範囲クエリー. キー
B-Tree でも Bε-Tree でも、ポイントクエリーはルートから正しい葉までの各ノードを訪問する。ただし Bε-Tree では、クエリーに応答するということは、この経路上のノードのバッファでメッセージを確認し、クエリーの結果を返す前に関連メッセージを適用することも意味する。たとえばキー
バッファの検索と挿入を効率的に行うために、通常各ノード内のメッセージバッファは赤黒木のような均衡のとれた二分探索木に編成されている。バッファ内のメッセージはターゲットキーとタイムスタンプの順にソートされる。タイムスタンプによってメッセージが正しい順序で適用される。したがってバッファへのメッセージの挿入、バッファ内の検索、および 1 つのバッファから別のバッファへのフラッシュはすべて高速である。
1.1 性能分析
この記事では B-Tree, Bε-Tree, LSM-Tree の動作を I/O の観点から分析する。我々の主な監視は RAM に収まらないほど大きなデータセットである。したがってパフォーマンスにもっとも大きく影響するのは、各操作を完了するために何回の I/O 要求を発光しなければならないかである。アルゴリズムの文献では、これはディスクアクセスマシン (DAM) モデル、外部メモリモデル、または I/O モデルとして知られている。
パフォーマンスモデル. B-Tree と Bε-Tree を単一のフレームワークで比較するためにいくつかの単純化された家庭を置く。すべての Key-Value ペアは同じサイズであり、ツリー内の各ノードは
このモデルはハードドライブや SSD などのブロックストレージデバイスの主要な性能特性に焦点を当てている。たとえばハードドライブの場合はこのモデルはノードを読み取るためにランダムシークのレイテンシーをキャプチャする。SSD の場合、このモデルは I/O 帯域幅コスト、つまり操作ごとにデバイスから読み取りまたは書き込みする必要のあるブロック数をキャプチャする。デバイスが帯域幅またはレイテンシのどちらに束縛されているかに関係なく、特定のノードサイズ
Bε-Tree の I/O パフォーマンス. Table 1 は B-Tree と Bε-Tree の各処理の漸近的な複雑性を示している。アップサート (upsert) とイプシロン (
B-Tree と Bε-Tree のポイントクエリーの複雑性はどちらもアイテム数の対数 (
範囲クエリーでは最初のキーに対して対数の検索コストが発生する。また範囲のサイズと、範囲がいくつのブロックデバイスにまたがって分散しているかに比例したコストが発生する。スキャンコストは読み取られたキーの数 (
書き込みの最適化. Bε-Tree や LSM-Tree のような書き込み最適化データ構造 (WODS; write-optimized data structures)) では小さなランダム挿入をバッチ処理することが不可欠である。WODS は、メッセージがツリーを移動するときに小さな書き込みを複数回発行する可能性があるが、I/O コストが大きなバッチで分割されると、挿入や削除の 1 回当たりのコストは操作ごとに 1 回の I/O よりも遙かに小さくなる。対照的に B-Tree へのランダム挿入のワークロードでは、新しい要素をターゲットの葉に書き込むために、挿入ごとに少なくとも一回の I/O が必要である。
Bε-Tree のフラッシュ戦略は常に大きなバッチで要素を移動できるように設計されている。ノードのバッファがいっぱいになり、
キャッシュ. ほとんどのシステムはツリーのサブセットを RAM にキャッシュする。LRU 置換ポリシーではツリーの最上部へのアクセスはキャッシュにヒットする可能性が高いが、葉や "下位ノード" へのアクセスはミスすることが多くなる。したがって、キャッシュがウォーム状態の場合、検索にかかる実際のコストは
1.2 ノードサイズ ( ) が性能に与える影響
B-Tree は挿入と範囲クエリーのコストのバランスを取るための小さなノードを持つ. B-Tree の実装では更新と範囲クエリーの性能のトレードオフに直面する。ノードサイズ
そのため多くの B-Tree 実装では小さなノード (数十から数百KB) が使用され、その結果、範囲クエリーのパフォーマンスが最適ではなくなる。ディスク上の空き領域が断片化されると B-Tree ノードもディスク上に散在する可能性がある。これはエージング (aging) と呼ばれることもある。これにより範囲クエリーはスキャンでそれぞれの葉をシークしなければならず帯域幅の使用率が悪化する。
例えばシーク時間が 5ms で帯域幅が 100MB/s のディスクに 4KB のノードが格納されている場合、1 つのキーを更新しても 4KB を書き換えるだけである。しかs範囲クエリーでは 4KB の葉ノードごとにシークを実行する必要があるため、ネット帯域幅は 800KB/s となり、ディスク本来の帯域幅の 1% 未満となる。
Bε-Tree はノードが大きい場合でも更新と範囲クエリーが効率的. 対照的に Bε-Tree でのバッチ処理では、
大きな
上記の挿入複雑度の比較では、
1.3 ε の役割
元々、Bε-Tree のパラメータ
直感的に、パラメータ
極端な例として、
最も興味深い構成は
挿入の場合、
他のすべてのパラメータが同じであると仮定すると、
上記の分析では、すべてのキーが単位サイズであり、ノードが
実際には、Bε-Tree の実装では固定の物理ノードサイズとファンアウト (
1.4 Bε-Tree を使用してアプリケーションを高速化する方法
上記の分析の実用的な結果は、Bε-Tree はポイントクエリーよりも更新を桁違いに高速化できると言うことである。この検索と挿入の非対称性は Bε-Tree 状のアプリケーションを設計する上で 2 つの意味を持つ。
パフォーマンスルール. 可能な限り更新前のクエリー (query-before-update) を避ける。
検索と挿入の非対称のため、一般的な読み取り-変更-書き込み (read-modify-write) (または読み取り-変更-挿入) パターンはクエリーの速度に束縛されるが、Bε-Tree が B-Tree より速くなることはない。
アップサート. Bε-Tree はこのような性能の非対称を埋めるのに役に立つ新しいアップサート (upsert) 操作をサポートしている。アップサートはキーの値を知らなくても発行できるコールバック関数を使用して更新をエンコードするメッセージの一種である。
アップサートは、キー、古い値、およびアップサートメッセージと一緒に格納できる補助データにのみ依存する変更を非同期でエンコードできる。墓石はアップサートの特殊なケースである。アップサートは、カウンターをインクリメント、ファイルのアクセス時間の更新、引き出し後のユーザの口座残高の更新、その他多くの操作にも使用できる。
アプリケーションはアップサートを使用してツリーに "アップサートメッセージ"
アップサートメッセージ
挿入メッセージや削除メッセージと同様に、ルートと葉の間で同じキーに対して複数のアップサートをバッファリングできる。複数のアップサートが保留中の状態でキーがクエリーされた場合、各アップサートはルートから葉へのパスで収集され、ツリーに挿入された順序でキーに適用されなければならない。
キー
パフォーマンスルール. 適切なインデックスを維持することで、挿入パフォーマンスを使用してクエリーパフォーマンスを向上する。
セカンダリインデックス. データベースではセカンダリインデックスによってクエリー大幅に高速化することができる。たとえば
B-Tree で複数のインデックスを保持する場合、各インデックスの更新には追加の挿入を必要とする。挿入はポイントクエリーと同じくらい高価であるため、各列にインデックスを維持することは現実的ではないことが多い。したがって、テーブル設計者は予想されるクエリーの種類やキーの分布などの要因を注意深く分析し、どの列にインデックスを設定するかを決定する必要がある。
Bε-Tree はこれらの問題を根本的に解決する。インデックスの維持コストは低い。ポイントクエリーは基本的に高価である ─ Bε-Tree のポイントクエリーは B-Tree よりも高速ではない。したがって Bε-Tree アプリケーションはクエリーを効率的に実行するために必要なインデックスを維持する必要がある。
適切な Bε-Tree インデックスを設計するために 3 つのルールがある。
まず、データベースのクエリーに使用するキーでソートされたインデックスを管理する。たとえば上記の例では、データベースは 2 つの Bε-Tree を維持する必要がある (1 つは
第二に、意図したクエリーに応答するために必要なすべての情報が各インデックスに含まれているようにする。例えばアプリケーションが
最後に、アプリケーションが可能な限り範囲クエリーを実行できるようにインデックスを設計する。例えばアプリケーションが
1.5 Log-Structured Merge Tree
Log-Structured Merge Tree (LSM-Tree) [7] は多くのバリエーションを持つ WODS である [8, 9]。通常、LSM-Tree 指数関数的に増加するサイズの B-Tree の対数で構成される。あるレベルのインデックスがいっぱいになると、次のインデックスにマージして空にする。各レベルが増加する係数は Bε-Tree の分岐計数 (
LSM-Tree は Bε-Tree と同じ挿入複雑度を持つように調整できるが、ナイーブに実装された LSM-Tree での問い合わせはクエリーを
LSM-Tree の問題の一つは、ブルームフィルタの利点が範囲クエリーにまで及ばないことである。ブルームフィルターはポイントクエリーを改善するためだけに設計されており範囲クエリーはサポートしていない。そのため LSM-Tree のすべてのレベルで範囲クエリーを実行する必要がある。これにより、Table 1 の検索オーバーヘッドが 2 乗され、Bε-Tree や B-Tree よりも明らかに劣る漸近的パフォーマンスが得られる。
LSM-Tree に対する Bε-Tree の 2 つ目の利点は、Bε-Tree ではアップサートを効率的に利用できることである。LSM-Tree でアップサートを使用すると、ブルームフィルタを追加することによるパフォーマンス上の利点が損なわれる。上述したように、アップサートは LSM-Tree を含むすべての WODS に共通する検索と挿入の非対称性に対処するものである。アプリケーションがアップサートを使用する場合、そのキーに対する保留中のメッセージを含むツリーのすべてのレベルにそのキーのメッセージが存在する可能性がある。そのため、後続のポイントクエリーでは、ツリーのすべてのレベルを問い合わせしなければならずブルームフィルタを追加する意味がなくなる。LSM-Tree のすべてのレベルを問い合わせると、Bε-Tree や B-Tree と比較してオーバーヘッドが 2 乗され、Bε-Tree のルートから葉までの経路をたどるよりもコストがかかることに注意。
要約すると、ブルームフィルタで強化された LSM-Tree は一部のワークロードで Bε-Tree の性能に匹敵するが、すべてのワークロードでそうはならない。Bε-Tree は LSM-Tree の性能を漸近的に上回る。特に、Bε-Tree はアップサートを多用するワークロードでの小さな範囲クエリーやポイントクエリーにおいて LSM-Tree より漸近的に高速である。
2 パフォーマンス比較
Bε-Tree がどのように機能するかを知るために、Bε-Tree をベースとしたカーネル内の研究用ファイルシステムである BetrFS のデータをいくつか紹介する。BetrFS を B-Tree で構築された btrfs など他のファイルシステムと比較する。より詳細な評価は最近の FAST 論文 [5] に掲載されている。
すべての実験結果は 4 コア 3.40 GHz Intel Core i7 CPU、4 GB RAM、250GB 7200 RPM ATA ディスクを搭載した Dell Optiplex 790 で収集した。各ファイルシステムは 4096 バイトのブロックサイズを使用した。システムは Linux カーネルバージョン 3.11.10 の 64 ビット Ubuntu 13.10 で実行された。核実験は BTRFS、ext4、XFS、ZFS などを含む複数の汎用ファイルシステムと比較した。エラーバーと±の範囲は 95% 信用区間を示す。特に明記されていない限りベンチマークはコールドキャッシュテストである。
小さな書き込み. 我々は TokuDB ベンチマーク [10] を使って 4 つのスレッド (1 CPU につき 1 スレッド) を使用し、ファンアウトが 128 の平衡ディレクトリツリー (balanced directory tree) に 200 バイトのファイルを 300 万個作成した。BetrFS では、ファイルの作成は Bε-Tree への挿入として実装され、小さなファイルの書き込みはアップサートとして実装されるため、このベンチマークはこれら 2 つの操作における Bε-Tree のパフォーマンスを示している。Figure 2 は各ファイルシステムでのファイル作成時の持続レート (sustained rate) を示している (対数スケールに注意)。ZFS の場合、ベンチマークを完了する前にファイルシステムがクラッシュしたため、実験を 5 回再実行し最も実行時間の長い反復のデータを使用した。BetrFS は最初から最も高速なファイルシステムの 1 つであり、実験中も良好なパフォーマンスを維持した。BetrFS の定常状態の性能は他のファイルシステムよりも桁違いに高速である。
この性能差は書き込み総数が少なく、バイトあたりのシーク数が少ないこと、つまり小さな書き込みの集約が優れていることの両方に起因する。blktrace のプロファイリングに基づくと、大きな違いの 1 つは書き込みバイト数である。BetrFS がディスクに書き込むそう MB は 4-10 倍少なく、書き込み要求の総数も 1 桁少なくなっている。他のファイルシステムでは ext4、XFS、ZFS がほぼ同じ量のデータを書き込んだが、基本的な書き込みスループットは大きく異なっていた。
局所性とディレクトリ操作. BetrFS では高速な範囲クエリーは高速な大規模ディレクトリスキャンに変換される。Table 2 は Linux 3.11.10 ソースツリー上でコールドキャッシュから find と grep -r を実行するのにかかった時間を示している。grep テストはファイルの内容を再帰的に検索し "cpu_to_be64" という文字列を探し、find テストは "wait.c" という名前のファイルを検索する。
find と grep の両ベンチマークでは、ファイルシステムのデータとメタデータが大きなノードに保存され、フルパスによって辞書式にソートされているため良好な結果が得られる。したがって関連するファイルはディスク上で互いに近くに保存される。BetrFS もメタデータのみを含む第 2 のインデックスも保持するため、メタデータのスキャンを範囲クエリーとして実装することができる。その結果、BetrFS は他のファイルシステムよりもディレクトリのメタデータとファイルデータの検索を 1~2 桁高速に行うことができる。
制限事項. BetrFS はまだ研究段階のプロトタイプであり、現在のところ大規模なディレクトリのリネーム、大規模な削除、大規模なシーケンシャル書き込みの 3 つの主要なケースにおいて他のファイルシステムよりもかなりパフォーマンスが悪いことに注意 (詳細は [5] にある)。BetrFS は名前の変更と削除を Bε-Tree 操作に直接マッピングしないため速度が低下する。シーケンシャル書き込みが遅い主な理由は、基礎となるフラクタルツリーにおいてすべてのデータをログに追加してからツリーに挿入するため、すべてのデータが少なくとも 2 回ディスクに書き込まれるためである。これらの問題は進行中の研究開発の取り組みで解決できると考えている。漸近分析によって裏付けられた我々の目標は、BetrFS がすべてのワークロードで他のファイルシステムに匹敵するかそれを上回ることである。
3 結論
Bε-Tree の実装は B-Tree の検索性能に匹敵し、挿入と削除を桁違いに高速化し、ディスク帯域幅に近い範囲クエリーを実行できる。Bε-Tree の設計と実装は、更新と範囲クエリのコストのトレードオフを、小さな更新と大きなノードをバッチ処理することで相互に有益な相乗効果に変換される。BetrFS を使った我々の結果は Bε-Tree の漸近的な改良により、Bε-Tree の性能特性に合わせて設計されたアプリケーションの実用的な性能向上をもたらすことを示している。
Acknowledgements
This work was supported in part by NSF grants CNS-1409238, CNS-1408782, CNS-1408695, CNS1405641, CNS-1149229, CNS-1161541, CNS-1228839, CNS-1408782, IIS-1247750, CCF-1314547, Sandia National Laboratories, and the Office of the Vice President for Research at Stony Brook University.
References
- G. S. Brodal and R. Fagerberg, “Lower bounds for external memory dictionaries,” in Proceedings of the 14th Annual ACM-SIAM Symposium on Discrete Algorithms (SODA), pp. 546–554, 2003.
- D. Comer, “The ubiquitous B-tree,” ACM Computing Surveys, vol. 11, pp. 121–137, June 1979.
- A. L. Buchsbaum, M. Goldwasser, S. Venkatasubramanian, and J. R. Westbrook, “On external memory graph traversal,” in Proceedings of the 11th Annual ACM-SIAM Symposium on Discrete Algorithms (SODA), pp. 859–860, 2000.
- Tokutek, Inc., “TokuDB: MySQL Performance, MariaDB Performance.” http://www.tokutek.com/products/tokudbfor-mysql/, 2013.
- W. Jannen, J. Yuan, Y. Zhan, A. Akshintala, J. Esmet, Y. Jiao, A. Mittal, P. Pandey, P. Reddy, L. Walsh, M. Bender, M. Farach-Colton, R. Johnson, B. C. Kuszmaul, and D. E. Porter, “BetrFS: A right-optimized write-optimized file system,” in Proceedings of the USENIX Conference on File and Storage Technologies (FAST), pp. 301–315, 2015.
- A. Aggarwal and J. S. Vitter, “The input/output complexity of sorting and related problems,” Communications of the ACM, vol. 31, pp. 1116–1127, Sept. 1988.
- P. O’Neil, E. Cheng, D. Gawlic, and E. O’Neil, “The log-structured merge-tree (LSM-tree),” Acta Informatica, vol. 33, no. 4, pp. 351–385, 1996.
- R. Sears and R. Ramakrishnan, “bLSM: a general purpose log structured merge tree,” in Proceedings of the 2012 ACM SIGMOD International Conference on Management of Data, pp. 217–228, 2012.
- P. Shetty, R. P. Spillane, R. Malpani, B. Andrews, J. Seyster, and E. Zadok, “Building workload-independent storage with VT-trees,” in Proceedings of the USENIX Conference on File and Storage Technologies (FAST), pp. 17–30, 2013.
- J. Esmet, M. A. Bender, M. Farach-Colton, and B. C. Kuszmaul, “The TokuFS streaming file system,” in Proceedings of the 4th USENIX Workshop on Hot Topics in Storage (HotStorage), June 2012.
翻訳抄
B-Tree と似た構造を持ち、更新と挿入で高い性能を持つ Bε-Tree に関する 2015 年の記事。
- BENDER, Michael A., et al. An Introduction to B-trees and Write-Optimization, login; magazine, 2015, 40.5.
- Ori Bernstein on An Introduction to Bε-trees and Write-Optimization [PWL NYC]