はじめに
Webアプリケーションのアクセス数やデータ量が増大するにつれ、直面しやすいボトルネックが 「データベース(RDB)のクエリ応答遅延」 です。
特に、数万件以上のレコードを保持するテーブルに対して適切な設計を行わずに検索や結合(JOIN)を実行すると、データベースサーバーのCPU使用率が100%に張り付き、システム全体がスローダウンする原因になります。
このパフォーマンス問題を劇的に解決するアプローチが インデックス(索引)設計 です。本記事では、データベースインデックスの仕組み、B-Treeインデックスの構造、そして効果的なインデックス設計ルールを解説します。
1. インデックスがクエリを高速化する理由
インデックスとは、書籍の巻末にある「索引」のようなものです。
もし索引がなければ、特定のキーワードを探すために本を最初のページから最後のページまで1行ずつ読まなければなりません。これをデータベースでは フルテーブルスキャン(All Table Scan) と呼びます。データ量が多ければ多いほど、莫大な処理時間がかかります。
インデックス(索引)を作成しておくと、データベースは事前に特定のカラムの値を並び替え(ソート)した状態で保持し、実データへのポインタ(住所)を記録します。これにより、必要なレコードを瞬時に検索できるようになります。
2. B-Treeインデックスの構造
リレーショナルデータベース(MySQL, PostgreSQLなど)で最も一般的に使用されるのが B-Tree (Balanced Tree) インデックス です。
B-Treeは、データをツリー状の階層構造で管理し、ルートノード、中間ノード、リーフノードで構成されています。
[ルートノード]
/ \
[100] [200]
/ \ / \
[...] [...] [...] [リーフノード] -> 実データ行への参照
検索の仕組み
- 最上部のルートノードから検索を開始します。
- 目的の値がノードの値より大きいか小さいかを比較し、辿るべき子ノード(ポインタ)を特定します。
- 最下部のリーフノードに達すると、そこに格納されている「テーブル上の物理的なデータ位置(ROWIDなど)」を取得し、一発で該当レコードを抽出します。
- メリット: データの総件数がどれほど増えても、ノードを辿る回数(計算量)は
O(log N)で済むため、常に数ミリ秒以下の高速な検索パフォーマンスを維持できます。
3. 効果的なインデックス設計の3大原則
すべてのカラムにインデックスを設定すれば良いかというと、そうではありません。インデックスには書き込み時(INSERT, UPDATE, DELETE)に索引自体を更新するコストがかかるため、多すぎると更新パフォーマンスが低下します。
インデックスを作成すべきかどうかの判断基準として、以下の3つが重要です。
原則1: カーディナリティ(選択度)の高いカラムを選ぶ
カーディナリティとは、「そのカラムに含まれる値のバリエーションの多さ」 です。
- カーディナリティが高い(インデックス推奨):
user_id、email、serial_numberなど(値の重複がほとんどない)。 - カーディナリティが低い(インデックス非推奨):
gender(性別)、status(有効/無効)など。性別でインデックスを作っても、全体データの半分がヒットしてしまうため、インデックスを辿るよりテーブル全体を直接スキャンした方が早くなり、インデックスが機能しません。
原則2: 複合インデックスは「左側のカラム」の順番を意識する
複数のカラムを組み合わせた 複合インデックス(Composite Index) を作成する場合、カラムを定義する順番が重要になります。
例えば、INDEX (category_id, created_at) という複合インデックスを作成した場合:
- 有効なクエリ:
WHERE category_id = 5(第1カラムが指定されている)WHERE category_id = 5 AND created_at > '2025-01-01'(両方指定されている) - 無効なクエリ (インデックスが使われない):
WHERE created_at > '2025-01-01'(第1カラムを指定せず、第2カラムだけで絞り込もうとしている)
複合インデックスは、「左側に指定したカラムから順に絞り込む」 クエリに対してしか機能しないというルールを覚えておきましょう。
原則3: JOINの結合キーやORDER BYの対象に設定する
検索条件(WHERE)だけでなく、テーブル同士を繋ぐ外部キー(JOIN ON)や、取得結果を並び替えるキー(ORDER BY)にもインデックスは絶大な効果を発揮します。ソート処理をメモリ上で行うコスト(Filesort)を排除し、処理を高速化できます。
まとめ
データベースのインデックス設計は、バックエンド開発におけるパフォーマンスチューニングの第一歩です。
- スロークエリを特定し、カーディナリティ(値の種類)の高いカラムにインデックスを設定する
- 複合インデックスはクエリの並び順(左側から一致)を意識して設計する
- 書き込み性能への影響を抑えるため、不要なインデックスは定期的に整理する
開発中は EXPLAIN コマンドを使用して、作成したインデックスが狙い通りクエリに適用されているかを常に確認する習慣を身につけましょう。
