チューニング
複合インデックスとカバリングインデックス
複合インデックスの列順序、左端一致ルール、I/O を大幅削減するカバリングの勘所。
複合インデックスは「左端から」使われる
複合インデックス (a, b, c) は左端の列から順にしか使われません。WHERE a = ? や WHERE a = ? AND b = ? では利用できますが、WHERE b = ? 単独では利用できない(または効率が下がる)のが基本です。
これを左端一致(left-prefix)ルールと呼びます。電話帳が「姓 → 名」の順でソートされているのと同じ仕組みで、「名」だけで探そうとしたら結局全部めくる必要があるのと一緒です。
列順序の決め方
複合インデックスの列順序は、以下の優先度で決めます:
- 等値検索される列を先頭に(
= ?) - その後に範囲検索される列(
>=,BETWEEN,IN) - 最後にソート / 取得用の列(
ORDER BY)
「カーディナリティ(種類数)が高い順」と覚えがちですが、実際のクエリパターン優先で判断してください。どれだけカーディナリティが高くても、そのクエリで使われない列を先頭にしたら意味がありません。
sql
-- (user_id, created_at) のインデックスは
-- このクエリで使われる (user_id は等値、created_at はソート)
SELECT * FROM orders
WHERE user_id = 42
ORDER BY created_at DESC LIMIT 10;
-- これでは使われにくい (user_id がない = 左端欠け)
SELECT * FROM orders
WHERE created_at >= '2026-01-01';カバリングインデックス
クエリで参照する全列がインデックスに含まれている場合、テーブル本体を読まずインデックスだけで結果を返せる(カバリング)。ヒープへのランダムアクセスが消え、I/O が大幅に減ります。
PostgreSQL なら INCLUDE 句で「キーには使わないが含めておく列」を追加できます(非キー列なのでソートには影響せず、インデックスサイズも抑えられる)。MySQL 8.0 / SQL Server も同様の機能あり。
EXPLAIN で Index Only Scan(PostgreSQL)や Using index(MySQL)が出ればカバリングが成立しています。
sql
-- PostgreSQL: INCLUDE で非キー列を追加
CREATE INDEX idx_orders_user_amount
ON orders (user_id) INCLUDE (amount, created_at);
-- このクエリがインデックスだけで完結
SELECT amount, created_at FROM orders WHERE user_id = 42;