| B+ツリー | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| タイプ | ツリー(データ構造) | |||||||||||||||||||||||
| ||||||||||||||||||||||||
B + ツリーは、ノードごとに可変だが多くの場合多数の子を持つm 分木です。B+ ツリーは、ルート、内部ノード、および葉で構成されます。 [ 1 ]ルートは、葉または 2 つ以上の子を持つノードのいずれかです。
B+ツリーは、各ノードにキーのみ(キーと値のペアではない)が含まれ、下部にリンクされた葉を持つ追加のレベルが追加されたBツリーと考えることができます。
B+ツリーの主な利点は、ブロック指向ストレージ環境、特にファイルシステムにおいて、効率的なデータ検索のためにデータを保存することにあります。これは主に、バイナリサーチツリーとは異なり、B+ツリーはファンアウト(ノード内の子ノードへのポインタの数、[ 1 ]通常100以上)が非常に高く、ツリー内の要素を見つけるために必要なI/O操作の数を減らすことができるためです。
B+ ツリーの概念を紹介する単一の論文はありません。その代わりに、すべてのデータをリーフ ノードに保持するという概念は、R. Bayer と E. McCreight によって導入された B ツリーの興味深い変種として繰り返し取り上げられています。[ 2 ] Douglas Comer は、B ツリーの初期調査 (B+ ツリーも対象としています) の中で、B+ ツリーが IBM のVSAMデータ アクセス ソフトウェアで使用されていたことを指摘し、1973 年に IBM が発表した記事に言及しています。[ 3 ]

他の木構造と同様に、B+木は、ルート、内部ノード(別名:内部ノード)、および葉ノードの3種類のノードの集合として表現できます。B +木では、これらのノードに対して以下の特性が維持されます。
ノードのポインタ特性を以下の表にまとめます。

定義上、B+ ツリーに含まれる各値は、ちょうど 1 つのリーフ ノードに含まれるキーです。各キーは、他のすべてのキーと直接比較可能である必要があり、これにより全順序が形成されます。[ 6 ]これにより、各リーフ ノードは常にすべてのキーをソートされた状態で保持することができ、各内部ノードは、特定のリーフに含まれる値の連続した範囲を表す区間の順序付きコレクションを構築できます。ツリーの上位にある内部ノードは、独自の区間を構築でき、これは、自身の子内部ノードに含まれる区間を再帰的に集約します。最終的に、B+ ツリーのルートは、ツリー内の値の全範囲を表し、すべての内部ノードは部分区間を表します。
この再帰的な区間情報を保持するには、内部ノードはさらに以下を含む必要があります。鍵のコピーのためには、インデックスiの子ノード(それ自体が内部ノードまたは葉ノードである可能性がある)がカバーする区間内の最小要素を表します。ここで、 m は特定の内部ノードの実際の子ノード数を表します。
B+ツリーの次数または分岐係数bは、内部ノードの容量、つまり直接の子ノードの最大許容数を表します。この値はツリー全体で一定です。インデックスレベルがhのb次数B+ツリーの場合:
B+ツリー内で値kを探しています。つまり、ルートから始めて、値kを含む可能性のあるリーフを探します。各ノードで、どの内部ノードをたどるべきかを判断します。B+ツリーの内部ノードは最大で 子ノードはそれぞれ異なる部分区間を表します。m個のエントリを線形探索して対応する子ノードを選択し、最終的に葉ノードに到達したら、そのn個の要素を線形探索して目的のキーを探します。ツリーの各段で全ての子ノードの1つの枝だけをたどるため、実行時間。ここで、Nは B+ ツリーの葉に格納されているキーの総数です。[ 4 ]
function search( k , root ) is let leaf = leaf_search(k, root) for leaf_key in leaf.keys(): if k = leaf_key: return true return false
function leaf_search( k , node ) is if node is a leaf: return node let p = node.children() let l = node.left_sided_intervals() assertlet m = p.len() for i from 1 to m - 1: if: return leaf_search(k, p[i]) return leaf_search(k, p[m])
この擬似コードでは、1から始まる配列インデックスを使用していることに注意してください。
B+の木は葉ではなく根から成長する。[ 1 ]
データレコードの集合が与えられたとき、特定のキーフィールドに基づいてB+ツリーインデックスを作成したいとします。一つのアプローチは、各レコードを空のツリーに挿入することです。しかし、各エントリごとにルートから適切なリーフページまで辿っていく必要があるため、この方法は非常にコストがかかります。効率的な代替手段として、一括ロードを使用する方法があります。
注記:
削除アルゴリズムの目的は、ツリー構造から目的のエントリノードを削除することです。適切なノードに対して削除アルゴリズムを再帰的に呼び出し、ノードがなくなるまで繰り返します。各関数呼び出しでは、インデックスを使用してノードを探し、削除してから、ルートまで遡って処理を行います。
削除したいエントリLについて:
注:マージはルートに伝播する可能性があり、その場合、高さが減少します。[ 10 ]

B+ツリーのリーフ(最下層のインデックスブロック)は、多くの場合、リンクリストで互いにリンクされています。これにより、範囲クエリやブロックの(順序付き)反復処理がより簡単かつ効率的になります(ただし、前述の上限は、この追加がなくても達成できます)。これにより、ツリーのスペース消費やメンテナンスが大幅に増加することはありません。これは、B+ツリーがBツリーよりも優れている点の1つを示しています。Bツリーでは、すべてのキーがリーフに存在するわけではないため、このような順序付きリンクリストを構築することはできません。したがって、B+ツリーは、データが通常ディスク上に存在するデータベースシステムのインデックスとして特に有用です。B+ツリーは、データ自体を格納するための効率的な構造を実際に提供できるからです(これは、[ 11 ]の238ページでインデックス構造「代替案1」として説明されています)。
ストレージシステムのブロックサイズがBバイトで、保存するキーのサイズがkである場合、おそらく最も効率的なB+ツリーは次のようになります。理論的にはこの一回限りのインデックスは不要ですが、実際にはインデックスブロック(例えば、リーフブロック内のリンクリスト参照)によって多少余分なスペースが占有されることがよくあります。インデックスブロックがストレージシステムの実際のブロックよりもわずかに大きいと、パフォーマンスが著しく低下するため、安全策をとる方が望ましいです。
B+ツリーのノードを要素の配列として構成すると、要素の挿入や削除にかなりの時間がかかる場合があります。これは、平均して配列の半分を移動する必要があるためです。この問題を解決するために、ノード内の要素を配列ではなく、二分木またはB+ツリーで構成することができます。
B+ツリーは、RAMに格納されたデータにも使用できます。この場合、ブロックサイズとして適切なのは、プロセッサのキャッシュラインのサイズです。
B+ツリーの空間効率は、いくつかの圧縮技術を用いることで向上させることができます。一つの方法として、各ブロックに格納されているキーをデルタ符号化で圧縮する方法があります。内部ブロックの場合、キーまたはポインタを圧縮することで空間を節約できます。文字列キーの場合、以下の手法を用いることで空間を節約できます。通常、内部ブロックのi番目のエントリには、ブロックの最初のキーが含まれています。完全なキーを保存する代わりに、ブロックの最初のキーの最短プレフィックスを保存することもできます。ブロックiの最後のキーよりも厳密に大きい (辞書順で)。ポインタを圧縮する簡単な方法もあります。連続するブロックがいくつかあると仮定すると、が連続して格納されている場合は、最初のブロックへのポインタと連続するブロックの数だけを格納すれば十分です。
上記すべての圧縮手法には、いくつかの欠点があります。まず、単一の要素を抽出するには、ブロック全体を解凍する必要があります。この問題を解決する手法の一つは、各ブロックをサブブロックに分割し、それぞれを個別に圧縮することです。この場合、要素の検索や挿入は、ブロック全体ではなくサブブロックのみを解凍または圧縮すれば済みます。また、圧縮手法のもう一つの欠点は、各ブロック内の要素の圧縮効率によって、格納される要素数がブロックごとに大きく異なる可能性があることです。
ReiserFS 、NSS、XFS、JFS、ReFS、およびBFSファイルシステムはすべて、メタデータインデックスにこのタイプのツリーを使用します。BFSはディレクトリを格納するためにB+ツリーも使用します。NTFSはディレクトリおよびセキュリティ関連のメタデータインデックスにB+ツリーを使用します。EXT4は、ファイルのエクステントインデックスにエクステントツリー(修正されたB+ツリーデータ構造)を使用します。[ 12 ] APFSは、ファイルシステムオブジェクトIDからディスク上の位置へのマッピングを格納し、ファイルシステムレコード(ディレクトリを含む)を格納するためにB+ツリーを使用しますが、これらのツリーのリーフノードには兄弟ポインタがありません。[ 13 ]
IBM Db2 [ 11 ] Informix [ 11 ] Microsoft SQL Server [ 11 ] Oracle 8 [ 11 ] Sybase ASE [ 11 ]およびSQLite [ 14 ]などのリレーショナルデータベース管理システムは、テーブルインデックスにこのタイプのツリーをサポートしていますが、各システムは、バリエーションと拡張を加えた基本的な B+ ツリー構造を実装しています。CouchDB [ 15 ] [ a ]やTokyo Cabinet [ 16 ]などの多くの NoSQL データベース管理システムも、データアクセスとストレージにこのタイプのツリーをサポートしています。
高次元データベース内で特定のクエリオブジェクトに匹敵するオブジェクトを見つけることは、このようなシステムで最も頻繁に使用されるものの、コストのかかる手順の1つです。このような状況では、B+ツリーを使用して最も近い近傍を見つけることが効果的です。[ 17 ]
B+ ツリーは、iDistance と呼ばれるインデックス付き検索方法を構築するために効率的に使用されます。iDistance は、高次元メトリック空間で k 個の最近傍 (kNN) を検索します。これらの高次元空間のデータは、空間またはパーティション戦略に基づいて分割され、各パーティションには、パーティションに関して近いインデックス値があります。ここから、これらのポイントは B+ ツリーを使用して効率的に実装できるため、クエリは単一次元の範囲検索にマッピングされます。言い換えれば、iDistance 技術は、シーケンシャル スキャンを高速化する方法と見なすことができます。データ ファイルの先頭から末尾までレコードをスキャンする代わりに、iDistance は、最近傍を非常に高い確率で早期に取得できる場所からスキャンを開始します。[ 18 ]
不揮発性ランダムアクセスメモリ(NVRAM)は、非静的な消費電力とセルメモリの高い堅牢性のため、モノのインターネット(IoT)システムの主要なメモリアクセス技術としてB+ツリー構造を使用しています。B +は、メモリへのデータのトラフィックを効率的に制御できます。さらに、使用頻度の高いリーフまたは参照ポイントの頻度に関する高度な戦略により、B+ツリーはデータベースシステムの耐久性を向上させる上で顕著な結果を示しています。[ 19 ]
{{cite book}}: CS1メンテナンス: 場所の発行元が見つかりません (リンク)