コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB

カバリングインデックス

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

カバリングインデックス

このレッスンで分かること

  • カバリングインデックスがテーブル参照をなくす仕組み
  • SELECT のカラム順とインデックスのカラム順の関係
  • INCLUDE 句と複合インデックスの違い

カバリングインデックス とは

テーブル本体を読まずに済む複合インデックス設計。本レッスンでは、カバリングインデックス の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

テーブル参照のおさらい

前のレッスンで触れた通り、セカンダリインデックスでの検索は次の 2 段階です。

  1. インデックスを辿って 主キー値 を得る
  2. その主キーで テーブル本体(クラスタードインデックス)を再走査 して行データを得る

この 2 段階目を テーブル参照 (Lookup) または ヒープ参照 と呼びます。1 件あたり数回の I/O が追加で発生し、件数が多いと累積コストが大きくなります。

例えば次のクエリです。

SQL クエリ

SELECT id, name FROM users WHERE email = 'alice@ex.com';

email インデックスから id を取り、その id で本体を引いて name を取る。テーブル参照が必須です。

カバリングインデックスとは

カバリングインデックス (Covering Index) とは、クエリが必要とする全カラムをインデックス内に含めた インデックスのことです。インデックスを引くだけで結果が完成し、テーブル参照が不要になります。

diagram (will load when visible)

先の例で (email, name) の複合インデックスを貼ると、name もインデックス内にあるため、テーブル参照は不要になります。

SQL クエリ

CREATE INDEX idx_email_name ON users (email, name); EXPLAIN SELECT id, name FROM users WHERE email = 'alice@ex.com'; -- Extra: Using index ← カバリングが効いている証拠

MySQL の EXPLAINExtra 列に Using index と出れば、インデックスのみで完結しています。

InnoDB なら主キーは常に含まれる

InnoDB のセカンダリインデックスは、リーフに 主キー値 を必ず含みます。よって SELECT id, email FROM users WHERE email = ? のように主キーと検索キーだけなら、(email) 単独のインデックスでもカバリング状態です。

これは InnoDB の重要な性質で、「SELECT id だけなら、ほぼ常にカバリングが効く」と覚えておくと役立ちます。

カバリングを意識した設計

頻出クエリを書き出し、必要カラムを全部含む複合インデックスを設計します。

SQL クエリ

-- 頻出クエリ SELECT user_id, status, total FROM orders WHERE user_id = ? AND created_at > ? ORDER BY created_at DESC; -- カバリング設計 CREATE INDEX idx_orders_user_created ON orders (user_id, created_at, status, total);

このインデックスは WHERE の絞り込み、ORDER BY のソート、SELECT の取得をすべてインデックス内で完結させます。テーブル参照ゼロです。

カラム順の原則

複合インデックスの カラム順 は次の優先順位で決めます。

  1. WHERE の等値比較 (=IN) のカラム
  2. ORDER BY / GROUP BY のカラム
  3. WHERE の範囲比較 (><BETWEEN) のカラム
  4. SELECT のカラム(カバリングのために含める)

(user_id, created_at, status, total) という順番には意味があります。user_id は等値、created_at は範囲 + ソートキー、後ろ 2 つは取得用、という具合に並べています。

「左端から条件を絞れる順 → ソート列 → 取得列」の順で複合インデックスを組む。

PostgreSQL の INCLUDE 句

PostgreSQL 11 以降は INCLUDE 句で「ソート不要な取得専用カラム」をインデックスに含められます。

SQL クエリ

CREATE INDEX idx_orders_user_created ON orders (user_id, created_at) INCLUDE (status, total);

statustotal はインデックスに含まれますが、ソートには使われません。B+tree のサイズを抑えつつカバリングを実現できる優れた機能です。

MySQL には同等機能がないため、通常の複合インデックスに含めます。

カバリングのコスト

カバリングは万能ではありません。

  • インデックスサイズが膨張 — 含めたカラム全部がインデックスに乗る
  • 書き込みコストが増加 — 含めたカラムが変わるたびにインデックス更新
  • メモリ消費 — 巨大インデックスはバッファプールを圧迫

100 文字の description カラムをインデックスに含めるのは多くの場合やりすぎです。短くて使用頻度の高い カラムだけ含めるのが定石です。

確認の仕方

MySQL なら EXPLAIN ... Extra: Using index、PostgreSQL なら EXPLAIN (ANALYZE)Index Only Scan と表示されれば成功です。

プレーンテキスト

PostgreSQL の例 Index Only Scan using idx_email_name on users Index Cond: (email = 'alice@ex.com') Heap Fetches: 0 ← 0 ならカバリング完全

Heap Fetches が大きいときは、PostgreSQL の VACUUM が走っていなくて Visibility Map が古い可能性があります。

やってみよう

  • 自分のプロジェクトで頻度トップ 3 のクエリを取り出し、各カラムを「絞り込み・ソート・取得」に分類する
  • それを満たすカバリングインデックスを設計する
  • インデックス追加前後の EXPLAIN 出力で Using index の有無を比較する

よくある質問

Q. インデックスを貼ると遅くなる処理はありますか?

A. INSERT / UPDATE / DELETE は遅くなります。インデックス自体も更新が必要なため、書き込みが多いテーブルに大量のインデックスを貼ると書き込み性能が落ちます。読み取りと書き込みのバランスで判断し、不要なインデックスは削除しましょう。

Q. 複合インデックスは順番が大事ですか?

A. 順番が極めて重要です。(user_id, created_at) のインデックスは WHERE user_id = ? AND created_at > ? で効きますが、WHERE created_at > ? だけだと効きません。よく使う絞り込みカラムを先頭に置くのが定石です。

Q. インデックスが効いているか確認するには?

A. EXPLAIN(MySQL/PostgreSQL)または EXPLAIN ANALYZE で実行計画を見ます。type が ref / range / const なら使われていて、ALL は全件スキャン(インデックス未使用)です。意図と違うインデックスが使われている場合は USE INDEX で強制することもできます。

次のレッスン

次は トランザクションとは で、複数の処理をまとめて安全に確定・取り消しする仕組みを学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. カバリングインデックス の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. カバリングインデックス とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン