コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
カバリングインデックス
カバリングインデックス
このレッスンで分かること
- カバリングインデックスがテーブル参照をなくす仕組み
- SELECT のカラム順とインデックスのカラム順の関係
- INCLUDE 句と複合インデックスの違い
カバリングインデックス とは
テーブル本体を読まずに済む複合インデックス設計。本レッスンでは、カバリングインデックス の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
テーブル参照のおさらい
前のレッスンで触れた通り、セカンダリインデックスでの検索は次の 2 段階です。
- インデックスを辿って 主キー値 を得る
- その主キーで テーブル本体(クラスタードインデックス)を再走査 して行データを得る
この 2 段階目を テーブル参照 (Lookup) または ヒープ参照 と呼びます。1 件あたり数回の I/O が追加で発生し、件数が多いと累積コストが大きくなります。
例えば次のクエリです。
SQL クエリ
SELECT id, name FROM users WHERE email = 'alice@ex.com';email インデックスから id を取り、その id で本体を引いて name を取る。テーブル参照が必須です。
カバリングインデックスとは
カバリングインデックス (Covering Index) とは、クエリが必要とする全カラムをインデックス内に含めた インデックスのことです。インデックスを引くだけで結果が完成し、テーブル参照が不要になります。
先の例で (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 の EXPLAIN で Extra 列に 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 の取得をすべてインデックス内で完結させます。テーブル参照ゼロです。
カラム順の原則
複合インデックスの カラム順 は次の優先順位で決めます。
WHEREの等値比較 (=、IN) のカラムORDER BY/GROUP BYのカラムWHEREの範囲比較 (>、<、BETWEEN) のカラム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);status と total はインデックスに含まれますが、ソートには使われません。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 つができることを確認しましょう。
- カバリングインデックス の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. カバリングインデックス とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン