最終更新:

【仕組み解説】データベースのインデックスはなぜ速い? — B-Treeの仕組みを図解


インデックス検索 vs フルテーブルスキャン B-Tree インデックス 50 20 | 35 65 | 80 5,10 25,30 40,45 55,60 70,75 85,90 WHERE id = 70 → 3回の比較でヒット! 計算量: O(log n) フルテーブルスキャン id=1 name=田中 id=2 name=佐藤 id=3 name=鈴木 ... id=70 name=山田 ← ヒット ... 残り全行もチェック 先頭から1行ずつ全件走査 計算量: O(n) 100万件のテーブルで id=70 を検索した場合 B-Tree: 約20回の比較 フルスキャン: 最大100万回
B-Treeインデックスとフルテーブルスキャンの比較
ひよこ ひよこ

データベースが遅いって話をよく聞くけど、インデックスを貼ると速くなるって本当?

ペンギン先生 ペンギン先生

本当だよ。まず「インデックスがない状態」を想像してみよう。100万行のテーブルから1件探すとき、DBは先頭から1行ずつ全部チェックしていく。これを「フルテーブルスキャン」というんだ。本で言えば、目次も索引もなしに1ページ目から全ページめくって探すようなものだね。

ひよこ ひよこ

それは確かに遅そう…。インデックスがあるとどう変わるの?

ペンギン先生 ペンギン先生

インデックスは、まさに「本の索引(さくいん)」と同じ仕組みだよ。索引には「キーワード → ページ番号」が並んでいるよね。DBのインデックスも「カラムの値 → 行の場所」を整理して保持しているんだ。だから全行を見なくても、索引をたどるだけで目的のデータに一発でたどり着ける。

ひよこ ひよこ

なるほど!でも索引ってどういう構造で整理されているの?

ペンギン先生 ペンギン先生

最も一般的なのが「B-Tree(ビーツリー)」という木構造だよ。根(ルート)から枝分かれしていって、葉(リーフ)に実際のデータへのポインタがある。たとえば100万件のデータでも、B-Treeなら約20回の比較で目的の行にたどり着ける。計算量でいうと O(log n) で、フルスキャンの O(n) と比べると桁違いに速いんだ。

ひよこ ひよこ

O(log n) ってことは、データが倍に増えても比較回数は1回しか増えないってこと?

ペンギン先生 ペンギン先生

その通り!100万件で約20回、200万件でも約21回。これがB-Treeの強さだね。ちなみに木の各ノードは複数のキーを持てるから、ディスクの読み取り回数も最小限に抑えられる設計になっているんだ。

ひよこ ひよこ

複合インデックスっていうのも聞いたことがあるけど、普通のインデックスと何が違うの?

ペンギン先生 ペンギン先生

複合インデックスは、複数のカラムをまとめて1つのインデックスにしたものだよ。たとえば「姓」と「名」で複合インデックスを作ると、「姓=田中 AND 名=太郎」の検索が1つのインデックスだけで済む。ただし順番が重要で、「姓, 名」の順で作ったインデックスは「姓」だけの検索にも使えるけど、「名」だけの検索には使えない。電話帳が「姓 → 名」の順で並んでいるのと同じ理屈だね。

ひよこ ひよこ

カバリングインデックスっていうのもあるって聞いたけど、それは何?

ペンギン先生 ペンギン先生

カバリングインデックスは、クエリが必要とするカラムがすべてインデックスに含まれている状態のことだよ。通常はインデックスで行の位置を見つけてから、テーブル本体にデータを取りに行く。でもカバリングインデックスなら、インデックスだけで結果が返せるからテーブルへのアクセスが不要になる。これが最速パターンだね。

ひよこ ひよこ

じゃあ全部のカラムインデックスを貼れば最強ってこと?

ペンギン先生 ペンギン先生

それが落とし穴なんだ。インデックスは「読み取りを速くする代わりに、書き込みを遅くする」というトレードオフがある。INSERT や UPDATE のたびに、テーブル本体だけでなくインデックスも更新しなきゃいけないからね。インデックスが10個あれば、1回のINSERTで11箇所(テーブルインデックス10個)を更新することになる。ストレージ容量も食うし、貼りすぎは逆効果だよ。

ひよこ ひよこ

むやみに貼っちゃダメなんだね…。効果があるか確認する方法ってあるの?

ペンギン先生 ペンギン先生

EXPLAINコマンドを使うといいよ。SQLの先頭に EXPLAIN をつけると、DBがそのクエリをどう実行するか(実行計画)を教えてくれる。「Seq Scan」と出たらフルスキャン、「Index Scan」と出たらインデックスが使われている。PostgreSQLなら EXPLAIN ANALYZE をつけると実際の実行時間も表示されるから、インデックスの効果を数値で確認できるんだ。

ひよこ ひよこ

B-Tree以外のインデックスもあるの?

ペンギン先生 ペンギン先生

あるよ。代表的なのが「ハッシュインデックス」だね。ハッシュ関数で値を変換して、直接データの場所を割り出す方式だ。完全一致検索(WHERE id = 100)なら O(1) で B-Tree より速い場合もある。ただし範囲検索(WHERE id BETWEEN 1 AND 100)や ORDER BY には使えないという大きな制約がある。だから汎用性の高い B-Tree がデフォルトで使われることがほとんどだね。

ひよこ ひよこ

インデックスの設計って奥が深いんだね…。実務ではどう判断すればいいの?

ペンギン先生 ペンギン先生

基本方針は3つ。まず WHERE 句や JOIN 条件に頻繁に使うカラムインデックスを貼ること。次に、カーディナリティ(値の種類数)が高いカラムを優先すること。性別のように2種類しかない列はインデックスの恩恵が薄い。最後に、定期的に EXPLAIN実行計画を確認して、不要なインデックスは削除すること。「必要なものだけを、必要な順序で」が鉄則だよ。