Web開発者のためのデータベースインデックス解説

Backend2026-09-13TryQuickToolBox

開発中はWebアプリがサクサク動いていても、データが増えるにつれて、かつてミリ秒で返っていたクエリが数秒かかるようになります。ユーザーは不満を言い、データベースのCPUが急上昇します。原因は多くの場合、インデックスの欠如か誤用です。インデックス作成はバックエンド開発者にとって最も効果の高いスキルの一つですが、しばしば誤解されています。このガイドでは、データベースインデックスの仕組み、いつ使うべきか、よくある落とし穴を避ける方法を説明します。

データベースインデックスとは?

インデックスは教科書の巻末にある索引のようなものだと考えてください。トピックを見つけるために全ページをスキャンする代わりに、索引で調べて、正しいページを指し示してもらいます。データベースインデックスも同様に機能します。つまり、データベースエンジンがテーブル全体をスキャンせずに行を素早く見つけるためのデータ構造です。

インデックスがない場合、SELECT * FROM users WHERE email = 'alice@example.com' のようなクエリは全テーブルスキャンを強制します。データベースは一致する行を見つけるまで全ての行を読み取ります。email にインデックスがあれば、データベースは一致する行に直接ジャンプできます。

インデックスの内部動作

ほとんどのリレーショナルデータベースは、デフォルトでB-tree(平衡木)インデックスを使用します。B-treeはデータをソートされた状態に保ち、検索、順次アクセス、挿入、削除を対数時間で行えます。これが、数百万行でもインデックス付きの検索が高速な理由です。

他のインデックスの種類には以下があります:

ほとんどのWebアプリケーションでは、B-treeインデックスが主力です。

インデックスを作成すべきタイミング

インデックスは無料ではありません。ストレージを消費し、書き込みを遅くします。戦略的に作成しましょう:

ただし、めったにクエリされない列や、他の列と組み合わせて使用されない限りカーディナリティが非常に低い列(例:ブールフラグ)にはインデックスを付けないようにしましょう。

インデックスの種類とユースケース

インデックスの種類 最適な用途 例
単一列 単純なフィルタ CREATE INDEX idx_email ON users(email);
複合 複数列でフィルタリングするクエリ CREATE INDEX idx_name_age ON users(last_name, first_name);
一意 一意性の強制 CREATE UNIQUE INDEX idx_username ON users(username);
部分 行のサブセットのインデックス作成 CREATE INDEX idx_active ON users(email) WHERE active = true;
カバリング インデックス列のみを必要とするクエリ CREATE INDEX idx_covering ON users(email, name);

インデックスの作成と検証方法

インデックスの作成は簡単です。たとえば、PostgreSQLでは:

CREATE INDEX idx_users_email ON users(email);

インデックスを作成した後、それが使用されていることを確認しましょう。EXPLAIN(または EXPLAIN ANALYZE)を使用してクエリプランを確認します:

EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'alice@example.com';

「Seq Scan」ではなく「Index Scan」または「Index Only Scan」を探しましょう。シーケンシャルスキャンが表示される場合、型の不一致、列に対する関数、または古い統計情報のためにインデックスが使用されていない可能性があります。

よくあるインデックスの間違い

  1. すべてにインデックスを付ける: インデックスが多すぎると書き込みが遅くなり、スペースを浪費します。
  2. 複合インデックスの順序を無視する: (a, b) の複合インデックスの場合、b だけでフィルタリングするクエリはインデックスを効率的に使用できません。
  3. インデックス列に関数を使用する: WHERE YEAR(created_at) = 2025 はインデックスの使用を妨げます。代わりに範囲条件を使用しましょう:WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'。
  4. 統計情報を更新しない: データベースは統計情報に依存してインデックスを選択します。定期的に ANALYZE を実行しましょう。
  5. 書き込みオーバーヘッドを見落とす: すべてのINSERT、UPDATE、DELETEはインデックスを更新する必要があります。書き込みが多いテーブルでは、選択的に行いましょう。

高度なテクニック

カバリングインデックス

カバリングインデックスはクエリに必要なすべての列を含むため、データベースはテーブルに触れることなくインデックスから直接データを取得できます。これにより、読み取りが多いクエリを劇的に高速化できます。

部分インデックス

行のサブセット(例:アクティブユーザー)を頻繁にクエリする場合、部分インデックスは完全なインデックスよりも小さく高速です。

インデックスのみのスキャン

一部のデータベースはインデックスのみのスキャンをサポートしており、必要なデータがすべてインデックス内にあります。これは最速のインデックスアクセスです。

インデックスの監視とメンテナンス

インデックスは更新や削除により時間とともに肥大化する可能性があります。PostgreSQLでは、VACUUM と REINDEX がパフォーマンスの維持に役立ちます。MySQLでは、OPTIMIZE TABLE がインデックスを再構築できます。スロークエリログを定期的に確認して、不足しているインデックスを特定しましょう。

FAQ

クエリがインデックスを使用しているかどうかを知るには?

クエリの前に EXPLAIN コマンド(または EXPLAIN ANALYZE)を使用します。出力は、データベースがインデックススキャンとシーケンシャルスキャンのどちらを使用しているかを示します。

インデックスが多すぎることはありますか?

はい。各インデックスは書き込み操作にオーバーヘッドを追加し、ストレージを消費します。読み取り/書き込みの比率に基づいてバランスを目指しましょう。

クラスタ化インデックスと非クラスタ化インデックスの違いは何ですか?

クラスタ化インデックスはテーブル内の行の物理的な順序を決定します(InnoDBの主キーのように)。非クラスタ化インデックスは行を指す別の構造です。テーブルは1つのクラスタ化インデックスしか持てませんが、多くの非クラスタ化インデックスを持つことができます。

データベースを最適化する準備はできましたか?まず、遅いクエリを分析し、必要な場所にインデックスを追加しましょう。素早いJSONフォーマットと検証には、私たちの JSON Formatter をお試しください — 無料で、完全にブラウザ内で動作します。