B木・B+木とデータベースインデックスの関係
概要¶
B木・B+木の仕組みと、なぜデータベースのインデックスに使われているかを解説した記事の紹介ポスト(sadkatwt の記事)。
詳細¶
B木(B-Tree)とは¶
自己平衡探索木の一種。通常の二分探索木と違い、各ノードが複数のキーと複数の子を持てる。
特性:
- 全ての葉が同じ深さ(平衡)
- ノードあたりのキー数: t-1 ≤ keys ≤ 2t-1(t = 最小次数)
- 検索・挿入・削除がすべて O(log n)
B+木(B+ Tree)とは¶
B木の変種で、データはすべて葉ノードにのみ格納 し、内部ノードはインデックスとしてのみ使う。さらに葉ノード同士がリンクリストで連結される。
なぜデータベースは B+木を使うのか¶
| 観点 | B木 | B+木 |
|---|---|---|
| 範囲検索 | 遅い(中間ノードも辿る) | 速い(葉を連結リストで辿るだけ) |
| ディスクI/O | 多い | 少ない(葉ノードに全データが集中) |
| フルスキャン | 非効率 | 効率的(葉を左から右に辿るだけ) |
| 空間効率 | 各ノードにデータ | 内部ノードにはキーのみ(軽量) |
MySQL InnoDB での B+木インデックス¶
-- クラスタインデックス(主キー = B+木のリーフにデータ行)
CREATE TABLE users (
id INT PRIMARY KEY, -- 主キー → クラスタインデックス
name VARCHAR(100),
email VARCHAR(200),
INDEX idx_email (email) -- セカンダリインデックス
);
-- セカンダリインデックス: 葉ノードに「email + 主キー」を格納
-- → email で検索 → 主キーを取得 → クラスタインデックスで行を取得(ダブルルックアップ)
カバリングインデックスで二重ルックアップを回避¶
-- email と name を一緒に返すクエリ
SELECT name FROM users WHERE email = 'alice@example.com';
-- email のみのインデックス → email検索 → 主キー取得 → 行取得 (2回のB+木ルックアップ)
INDEX idx_email (email)
-- カバリングインデックス → B+木の葉ノードだけで完結(1回のルックアップ)
INDEX idx_email_name (email, name)
B+木の高さとアクセス数¶
ページサイズ 16KB、キーサイズ 8バイト、ポインタ 6バイトとすると:
- 1ノードに約 16384 / (8+6) ≈ 1170 キーが入る
- 高さ2のB+木: 1170² ≈ 137万件 をカバー
- 高さ3のB+木: 1170³ ≈ 16億件 をカバー
→ 大規模テーブルでも 2〜3回のディスクI/O で目的の行にたどり着ける
なぜ重要か / いつ使うか¶
- インデックスが効くクエリの設計: WHERE 句・ORDER BY・JOIN の列にインデックスを張る意味を理解できる
- EXPLAIN の読み方:
Using index(カバリング)vsUsing index conditionの違いを把握できる - 複合インデックスの列順: 左端から使われる性質(Leftmost prefix rule)の理由が分かる
- 面接での必須知識: 「なぜインデックスを使うと速いのか」に B+木の構造で答えられる