Mysqlインデックス構造でB +番号を使用する理由

序文

MySQLでは、InnodbであろうとMyIsamであろうと、B +ツリーがインデックス構造として使用されます(ハッシュなどの他のインデックスはここでは考慮されません)。この記事では、最も一般的なバイナリ検索ツリーから始めて、MySQLがインデックス構造としてB +ツリーを選択する理由を説明するために、さまざまなツリーによって解決される問題とそれらが直面する新しい問題を段階的に説明します。328ページのMySQLPDFドキュメントを整理しました

1.バイナリ検索ツリー(BST):不均衡

バイナリソートツリーとも呼ばれるバイナリ検索ツリー(BST、バイナリ検索ツリー)は、バイナリツリーに基づいて満たす必要があります:任意のノードの左側のサブツリーのすべてのノード値は、ルートノードの値以下であり、任意のノードの右側のサブツリー上記のすべてのノードの値は、ルートノードの値以上です。以下はBSTです。

Mysqlインデックス構造でB +番号を使用する理由

クイック検索が必要な場合、クエリ時間はツリーの高さに依存し、平均時間の複雑さはO(lgn)であるため、BSTにデータを保存するのが一般的な選択です。ただし、次の図に示すように、長いスキューのためにBSTが不均衡になる可能性があります。このとき、BSTはリンクされたリストに縮退し、時間の複雑さはO(n)に縮退します。

この問題を解決するために、バランスの取れたバイナリツリーが導入されました。

Mysqlインデックス構造でB +番号を使用する理由

2.バランスバイナリツリー(AVL):回転には時間がかかります

AVLツリーは厳密にバランスの取れたバイナリツリーです。すべてのノードの左右のサブツリー間の高さの差は1を超えることはできません。平均および最悪の場合、AVLツリーの検索、挿入、および削除はすべてO(lgn)です。

バランスをとるためのAVLの鍵は、ローテーション操作にあります。挿入と削除により、バイナリツリーのバランスが崩れる可能性があります。この場合、1つ以上のツリーローテーションによってツリーのバランスを取り直す必要があります。データを挿入する場合、最大で1回転(1回転または2回転)しか必要ありませんが、データを削除すると、ツリーのバランスが崩れます。AVLは、削除されたノードからルートノードまでのパス上のすべてのノードのバランスを維持する必要があります。回転の大きさはO(lgn)です。

ローテーションに時間がかかるため、AVLツリーはデータを削除するときに非常に非効率的です。削除操作が多い場合、バランスを維持するためのコストがそれがもたらすメリットよりも高くなる可能性があるため、AVLは実際には広く使用されていません。

3.赤と黒の木:木が高すぎる

AVLツリーと比較して、赤黒ツリーは厳密なバランスを追求しませんが、大まかなバランスを追求します。ルートからリーフまでの可能な最長パスが、可能な最短パスの2倍以下になるようにします。実装の観点から、赤黒ツリーの最大の特徴は、各ノードが2つの色(赤または黒)のいずれかに属し、ノードの色の分割が特定のルールを満たす必要があることです(特定のルールは省略されています)。赤黒の木の例は次のとおりです。

Mysqlインデックス構造でB +番号を使用する理由

AVLツリーと比較すると、ツリーのバランスが悪くなり、高さが高くなるため、赤黒ツリーのクエリ効率が低下します。ただし、赤黒の木も色を導入するため、赤黒の木の削除効率が大幅に向上します。データの挿入または削除時には、基本的なバランスを確保するためにO(1)の回転と色の変更のみが必要です。AVLのような必要はありません。ツリーはO(lgn)回回転します。一般に、赤黒の木の統計的パフォーマンスはAVLよりも高くなります。

したがって、実際のアプリケーションでは、AVLツリーの使用は比較的少なく、赤黒ツリーの使用は非常に広範囲です。たとえば、JavaのTreeMapは赤黒ツリーを使用してソートキーと値のペアを格納します。Java8のHashMapはリンクリスト+赤黒ツリーを使用してハッシュの競合の問題を解決します(競合するノードが少ない場合はリンクリストを使用し、競合するノードが多い場合は赤を使用します)黒い木)。

メモリ内のデータ(上記のTreeMapやHashMapなど)の場合、赤黒ツリーのパフォーマンスは非常に優れています。ただし、ディスクなどの補助ストレージデバイス(MySQLなどのデータベースなど)のデータの場合、赤黒ツリーはまだ高すぎるため、赤黒ツリーは得意ではありません。データがディスクにある場合、ディスクIOが最大のパフォーマンスボトルネックになります。設計目標はIOの数を最小限に抑えることであり、ツリーの高さが高いほど、追加、削除、変更、およびチェックに必要なIO時間が長くなり、パフォーマンスに深刻な影響を及ぼします。

4、Bツリー:ディスクのために生まれた

Bツリー(マイナス記号ではない)とも呼ばれるBツリーは、ディスクなどの補助ストレージデバイス用に設計された多方向のバランスの取れた検索ツリーです。バイナリツリーと比較すると、ツリーの各非リーフノードは複数のサブツリーを持つことができます。したがって、ノードの総数が同じである場合、Bツリーの高さはAVLツリーおよび赤黒ツリー(Bツリーは「ショートファットマン」)よりもはるかに低くなり、ディスクIOの数は大幅に削減されます。

Bツリーを定義するための最も重要な概念は次数です。次数mのBツリーの場合、次の条件を満たす必要があります。

  • 各ノードには、最大m個の子ノードが含まれます。

  • ルートノードに子ノードが含まれている場合、少なくとも2つの子ノードが含まれます。ルートノードを除いて、各非リーフノードには少なくともm / 2の子ノードが含まれます。

  • k個の子ノードを持つ非リーフノードには、k-1個のレコードが含まれます。

  • すべてのリーフノードは同じレイヤーにあります。

Bツリーの定義は、主に子ノードの数と非リーフノードのレコードの数を制限していることがわかります。

次の図は、3次のBツリーの例です。

Mysqlインデックス構造でB +番号を使用する理由

ツリーの高さが小さいことに加えて、Bツリーの利点はアクセスローカリティの原則を使用することです。いわゆる局所性の原則は、データの一部が使用される場合、近くのデータが短時間で使用される可能性が高くなることを意味します。Bツリーは、同じノードに同様のキーを持つデータを格納します。データの1つにアクセスすると、データベースはノード全体をキャッシュに読み込みます。隣接するデータにすぐにアクセスすると、キャッシュに直接読み込むことができます。 、ディスクIOは必要ありません。つまり、Bツリーのキャッシュヒット率が高くなります。

Bツリーには、mongodbインデックスがBツリー構造を使用するなど、データベースにいくつかのアプリケーションがあります。しかし、多くのデータベースアプリケーションでは、Bツリーの変形であるB +ツリーが使用されます。

5、B +ツリー

B +ツリーも多方向のバランスの取れた検索ツリーであり、Bツリーとの主な違いは次のとおりです。

  • Bツリーの各ノード(リーフノードと非リーフノードを含む)は実際のデータを格納します。B+ツリーでは、リーフノードのみが実際のデータを格納し、非リーフノードはキーのみを格納します。MySQLでは、ここで言及されている実際のデータは、行のすべてのデータ(Innodbのクラスター化されたインデックスなど)、または行のプライマリキー(Innodbのセカンダリインデックスなど)、または行のアドレス(MyIsamなど)の場合があります。非クラスター化インデックス)。

  • Bツリーのレコードは1回だけ表示され、繰り返し表示されませんが、B +ツリーのキーは繰り返し表示される場合があります。リーフノードに表示されるか、非リーフノードに繰り返し表示される必要があります。

  • B +ツリーのリーフノードは、二重にリンクされたリストによってリンクされています。

  • Bツリーの非リーフノードの場合、レコードの数は子ノードの数より1少なくなりますが、B +ツリーのレコードの数は子ノードの数と同じです。

したがって、Bツリーと比較して、B +ツリーには次の利点があります。

  • IO時間の短縮: B +ツリーの非リーフノードにはキーのみが含まれ、実際のデータは含まれないため、各ノードに格納されるレコードの数はBの数よりもはるかに多い(つまり、次数mが大きい)ため、B +ツリーの高さは高くなります。アクセスに必要なIO時間は短く、少なくなります。さらに、各ノードがより多くのレコードを格納するため、アクセスローカリティの原則がより有効に活用され、キャッシュヒット率が高くなります。

  • 範囲クエリに適しています: Bツリーで範囲クエリを実行する場合は、最初に検索する下限を見つけてから、上限が見つかるまで順番にBツリーをトラバースします。B+ツリー範囲クエリの場合は、リンクされたリストをトラバースするだけで済みます。それでおしまい。

  • より安定したクエリ効率: Bツリーのクエリ時間の複雑さは1からツリーの高さ(それぞれルートノードとリーフノードに対応)の間ですが、B +ツリーのクエリの複雑さは、すべてのデータがリーフノード。

B +ツリーには欠点もあります。キーが繰り返されるため、より多くのスペースを占有します。ただし、パフォーマンス上の利点と比較すると、スペースの欠点は許容できる場合が多いため、B +ツリーはBツリーよりもデータベースで広く使用されています。

6. B +ツリーの力を感じる

前述のように、赤黒の木などのバイナリツリーに対するBツリー/ B +ツリーの最大の利点は、ツリーの高さが小さいことです。実際、InnodbのB +インデックスの場合、ツリーの高さは通常2〜4レベルです。具体的な見積もりをしてみましょう。

ツリーの高さは順序によって決まり、順序が大きいほどツリーは短くなります。順序のサイズは、各ノードが保存できるレコードの数によって異なります。Innodbの各ノードはページ(ページ)を使用し、ページのサイズは16KBで、メタデータは約128バイト(ファイル管理ヘッダー情報、ページヘッダー情報などを含む)しか占有せず、スペースのほとんどはデータの格納に使用されます。

  • 非リーフノードの場合、レコードにはインデックスのキーと次のノードへのポインタのみが含まれます。リーフ以外の各ノードページに1000レコードが格納されていると仮定すると、各レコードは約16バイトを占有します。この仮定は、インデックスが整数またはそれより短い文字列の場合に妥当です。それを拡張するために、インデックス列の長さが長すぎないようにするという提案をよく耳にします。これが理由です。インデックス列が長すぎ、各ノードに含まれるレコードの数が少なすぎるため、ツリーが高すぎて、インデックスの効果が大幅に低下します。そして、インデックスはより多くのスペースを浪費します。

  • リーフノードの場合、レコードにはインデックスのキーと値が含まれ(値は行のプライマリキー、行の完全なデータなどです。詳細については上記を参照してください)、データ量が多くなります。各リーフノードページには100レコードが格納されていると想定されます(実際、インデックスがクラスター化インデックスの場合、この数は100未満になる可能性があります。インデックスがセカンダリインデックスの場合、この数は100をはるかに超える可能性があります。実際の状況に応じて推定できます)。 。

3層のB +ツリーの場合、最初の層(ルートノード)には1ページあり、1000レコードを格納できます。2番目の層には1000ページあり、1000 1000レコードを格納できます。3番目の層(リーフノード)には1000があります。 1000ページ、各ページは100レコードを格納できるため、1000 1000 100レコード、つまり1億を格納できます。バイナリツリーの場合、1億レコードを保存するには約26層かかります。

セブン、まとめ

最後に、さまざまなツリーによって解決された問題と、それらが直面する新しい問題を要約します。

  1. バイナリ検索ツリー(BST):並べ替えの基本的な問題を解決しますが、バランスを保証できないため、リンクされたリストに縮退する可能性があります。

  2. バランスバイナリツリー(AVL):バランスの問題は回転によって解決されますが、回転操作の効率が低すぎます。

  3. 赤黒ツリー:厳密なバランスを放棄し、赤と黒のノードを導入することで、AVL回転効率が低いという問題は解決されます。ただし、ディスクなどのシナリオでは、ツリーが高すぎ、IOの数が多すぎて328ページのMySQLPDFドキュメント整理できません

  4. Bツリー:バイナリツリーをマルチパスバランス検索ツリーに変更することで、ツリーが高すぎるという問題が解決されます。

  5. B +ツリー:Bツリーに基づいて、非リーフノードはデータを格納しない純粋なインデックスノードに変換されます。これにより、ツリーの高さがさらに低くなります。さらに、リーフノードはポインタを使用してリンクリストに接続され、範囲クエリがより効率的になります。

おすすめ

転載: blog.51cto.com/14975073/2587408