
はじめに
こんにちは、iimonエンジニアのみやこしです、業務の中でSQLを扱う機会が多く、勉強を進めるうちに INDEXの構造 にとても興味を持ちました。 今回は、MySQLのINDEXである B+treeの基本的な構造 について紹介したいと思います。
インデックスとは
インデックス(Index) とは、データベースのテーブルに対して検索を高速化するための仕組みです。 本でいう「目次」や「索引」のような役割を持ち、テーブルの中から目的の行を効率よく探し出せるようにします。MySQLのインデックスはデータベースエンジンのInnoDBで実現して、主に B+Tree が使われています。
二分探索木
インデックスを理解するうえで「二分探索木」の基本について少し紹介します。二分探索木は下記の図のように、各ノードが最大で2つの子ノード(左と右)を持つ木構造で、探索するときは「大小比較」で辿るだけなので効率的です。

しかし昇順だけを順番に入れていくと、ただの線形リストみたいになり、効率が落ちます。
二分探索木を詳しく知りたい方は下記の記事を参考にしてみてください!
B-Treeの構造を見ていく
B-Treeインデックスは、二分探索木のアイデアを発展させたものになります。 B-Treeは、ルートノード(木の一番上にあり必ず存在する)、内部ノード(途中にあり検索の分岐点となる)、リーフノード(葉)(一番下にあり子を持たない)という3種類のノードで構成されています。
各ノードには キー と ポインタ が含まれます。
キーは昇順に並んでおり、データを格納しています。
ポインタは子ノードへの参照を保持します。

下記の図は最大次数(max-degree)が5のB-treeを例にしています(各ノードは最大4つのキーと5つのポインタを持てます)
※緑の部分はデータ、青の部分はキーになります。

B-Tree では、データの追加や削除が行わらたとき、分割やマージが発生します。これはB-Tree のバランスを保ち、効率的な検索をできるようにしています。
※最大次数(max-degree)が4のB-tree
ノードが最大容量に達した場合に分割が発生します。

※データ333を探す時

このように、B-Treeは1つのノードに複数のキーとポインタを格納できるため、二分探索木よりも木の高さを低く抑えることができます。 探索は「ノード内のキーを比較し、範囲に応じて子ノードへ移動する」という処理を繰り返して行われます。 さらに、葉ノードはすべて同じ高さに揃っているため、探索時間は常に一定に保たれるというメリットがあります。
B+Treeの構造を見ていく
B+Tree は、 B-Treeをさらに発展させたものになります。具体的な違いを見ていきましょう。
※緑の部分はデータになります。

B-treeのすべてのノード(内部ノード & 葉ノード) にデータ(キーと値)が格納されるに対してB+Tree は葉ノードにのみ格納されます。そのため、探索の処理は必ず葉ノードまで進み、動作がシンプルかつ一定時間で完了します。(キー29が内部ノードに存在する時、同じキー29が必ず葉ノードにも存在する)
内部ノードにはデータを格納せずキーだけを持つため、1ノードあたりにより多くのキーを収容できます。その結果、木の高さを抑えられ、大量データでも少ないステップで探索が可能です。
葉ノード同士が連結リストでつながっているため、ある値を見つけたあとは横にたどるだけで効率よく範囲検索できます。
※B+treeの分割とマージ

※データ211を探す時

SQLの BETWEENなどが最適です。例えば下記のSQLでは39〜62の範囲検索になります。
SELECT * FROM students WHERE score BETWEEN 38 AND 62;
B+Treeインデックスでの処理の流れ
B+Tree のルートノードから探索を始め、38 以上の最初の値までたどり着きます。
次は リーフノードの連結リストを横にスキャンします。リーフノードにはデータが昇順で並び、さらにノード同士が連結されているので、38の位置から、次のノードへ、さらに次のノードへと順にたどるだけで 62 を取得できます。
62を超えた時点で終了します。
上記の検索B-treeだとデータが異なるノードに分散されているため、個別でノードを検索するので、「木をたどり直す」必要があります。
インデックスの種類
InnoDBストレージエンジンにおいて、インデックスの保存形式は「クラスター化インデックス」「非クラスター化インデックス」の2種類があります。
クラスター化インデックス
テーブルの実データとインデックスが同じ場所に保存されます。
インデックスの一番下(葉ノード)には、実際の行データが入っています。
必ず1つだけ存在します。
つまり「テーブルそのものがクラスタ化インデックス」になっています。 主キーがあるテーブルでは、その主キーが自動的にクラスタ化インデックスになります。
非クラスター化インデックス
インデックスと実データは別の場所に保存されます。
葉ノードには「行データそのもの」ではなく、主キーへの参照が入っています。
1つのテーブルに複数作ることが可能です。
データを探すときは、まず非クラスタ化インデックスで「主キー」を見つけ、そこからクラスタ化インデックスを使って実際のデータを取りに行きます。(この流れを「ダブルルックアップ」と呼ぶこともあります)
イメージ例📕:
みたいな感じです✨
図を見てもう少し理解を深めましよう〜

赤い矢印で示されている部分はクラスタ化インデックスになります、主キー(id)を基準にした B+Tree の構造です。各葉ノード(最下層)には、行データそのもの(row)が格納されています。
緑の矢印で示されている部分は非クラスタ化インデックスになりますname カラムを基準にしたインデックスです。各葉ノードには name の値と一緒に、対応する主キー(id) が格納されています。
例えば下記のSQLを実行した時に実際どのよう流れで処理するか見ていきます
select * from user where name = 'Arm';

name = 'Arm' を非クラスタ化インデックスで探索し、対応する主キー(id=10)を取得。 その後クラスタ化インデックスにアクセスし、主キー id=10 をB+Treeで探索。 最後に葉ノードから行データ(Armのレコード)を取得します。
「クラスター化インデックス」と「非クラスター化インデックス」を理解すると、今回の例では
select * from user where id = 10;
のように主キー検索を行う方が、行データを直接保持しているクラスタ化インデックスにアクセスできるため、高速に処理されることが分かります。
最後に
今回の勉強を通じて、インデックスの仕組みの基本とSQLの高速化の理由を理解できました。これからSQLを使う際にインデックスを意識して活用していきたいです。 ここまでお読みいただき、ありがとうございます。
弊社ではエンジニアを募集しております。
この記事を読んで少しでも興味を持ってくださった方は、ぜひカジュアル面談でお話ししましょう!
下記リンクよりご応募お待ちしております!
iimon採用サイト / Wantedly / Green
参考文献
B-Tree インデックスの基礎|【データベース基礎】インデックスの仕組みを理解する(初学者向け)
B+Tree インデックスの基礎|【データベース基礎】インデックスの仕組みを理解する(初学者向け)
SQL Serverのインデックスの仕組みを理解する | cloud.config Tech Blog
画像参照: 黑马程序员 MySQL数据库入门到精通,从mysql安装到mysql高级、mysql优化全囊括_哔哩哔哩_bilibili