B-treeインデックスの仕組み
「インデックスを貼れば速くなる」という話は知っていても、なぜ速くなるのかを説明できる人は意外と少ないものです。仕組みを理解すると、どのカラムにインデックスを貼るべきか、なぜ効かない場合があるのかを自分で判断できるようになります。
B-tree とは
B-treeインデックス? MySQLのデフォルトのインデックス構造。バランス木(Balanced Tree)で値を管理し、等値検索・範囲検索・ORDER BY の高速化に有効。ほとんどのユースケースでこれを使う。 は「バランス木(Balanced Tree)」の略で、MySQL(InnoDB)が インデックス? テーブルの検索を高速化するためのデータ構造。本の索引のように、特定の列の値から行の位置をすばやく特定できる。検索は速くなるが、書き込みはやや遅くなる。 の実装に使っているデータ構造です。
木構造の特徴は検索の効率にあります。100万行のテーブルを線形探索(端から1件ずつ確認)すると最大100万回の比較が必要ですが、B-treeを使うと約20回の比較で見つかります。これは log₂(1,000,000) ≈ 20 という対数の性質によるものです。
B-treeの構造
B-treeは以下の3種類のノードで構成されます。
- ルートノード:木の根。検索はここから始まる
- 内部ノード:分岐点。キー値と子ノードへのポインタを持つ
- リーフノード:末端。実際のデータへのポインタを持ち、互いにリンクされている
リーフノードが双方向にリンクされている点が重要です。これにより範囲検索が効率的になります。
ルート: [50]
/ \
内部: [20,30] [70,90]
/ | \ / | \
リーフ: [10,15] [20,25] [30,35] ...(双方向リンク)
インデックスの物理構造(InnoDB)
InnoDB では2種類のインデックス構造があります。
クラスタインデックス(プライマリキー)
InnoDB のテーブルはプライマリキー順にデータが物理的に並んでいます。リーフノードに実際の行データが格納されているため、プライマリキーでの検索は非常に高速です。
-- プライマリキー検索は最速
SELECT * FROM users WHERE id = 42;
セカンダリインデックス
プライマリキー以外のカラムに貼るインデックスです。リーフノードにはプライマリキーの値が格納されています。
-- email にセカンダリインデックスがある場合
SELECT * FROM users WHERE email = 'alice@example.com';
この検索は2段階になります:
- email インデックスから
id = 42を取得 - クラスタインデックスから
id = 42の行データを取得(ブックマークルックアップ)
等値検索・範囲検索・ソートへの効果
等値検索
SELECT * FROM products WHERE sku = 'ABC-001';
B-treeをルートから辿り、該当のリーフノードを O(log n) で特定します。
範囲検索
SELECT * FROM orders WHERE created_at BETWEEN '2026-01-01' AND '2026-03-31';
開始値をB-treeで特定したあと、リーフノードのリンクを辿って終了値まで順に読みます。インデックスなしだとテーブル全体を舐める必要があります。
ソート
SELECT * FROM users ORDER BY last_name;
last_name にインデックスがあると、リーフノードはすでにソート済みです。EXPLAIN の Extra に Using filesort が出ていたら、インデックスでソートが完結していない証拠です。
カバリングインデックス
カバリングインデックス? クエリで必要な全カラムがインデックスに含まれていて、テーブル本体を参照せずに結果を返せる状態。EXPLAINのExtraに`Using index`と表示される。非常に高速。 とは、クエリが必要とするすべてのカラムがインデックスに含まれている状態です。セカンダリインデックスの場合、通常はインデックスでプライマリキーを見つけてから元テーブルにアクセスしますが、必要なカラムがすべてインデックスに入っていれば元テーブルへのアクセスが不要になります。
-- name と email だけ取得する場合
SELECT name, email FROM users WHERE name = 'Alice';
-- name と email を含む複合インデックスを作成
ALTER TABLE users ADD INDEX idx_name_email (name, email);
EXPLAIN の Extra に Using index と表示されればカバリングインデックスが効いています。これは最も高速な状態です。
いつインデックスを作るべきか
インデックスはタダではありません。以下のコストがあります:
- ディスク容量:インデックスの分だけ容量が増える
- INSERT/UPDATE/DELETE の遅延:データ変更時にインデックスも更新が必要
- メモリ:InnoDB のバッファープールでインデックスをキャッシュするメモリが必要
インデックスを作るべきケース
WHERE、JOIN ON、ORDER BY、GROUP BYで頻繁に使われるカラム- カーディナリティ(値の種類数)が高いカラム(ユーザーID、メールアドレスなど)
- 外部キー
インデックスを作らなくていいケース
- カーディナリティが低いカラム(ステータスが3種類しかないなど)
- ほとんどアクセスされないカラム
- 小さなテーブル(数千行以下ならフルスキャンも十分速い)
次のエピソードでは、インデックスを貼ったのになぜか使われない——そういうケースを体系的に学びます。