複合インデックスの「順番」、適当に決めてない?
現場でコードレビューをしていると、複合インデックス(マルチカラムインデックス)を作るときに「とりあえず使われる列を全部ぶち込んでおくか」という設計をよく見かけるんだよね。
正直に言うと、それ、半分正解で半分は「宝の持ち腐れ」だよ。
PostgreSQLのインデックスは、ただの「辞書」だと思えばわかりやすい。辞書を引くときに「あいうえお順」で探すのと、「ランダムに並んだ単語」を探すのとでは、スピードが天と地ほど違うよね。今回は、PostgreSQLでパフォーマンスを劇的に変える「複合インデックスの列順序」の秘訣を伝授するよ。
—
なぜ「順序」が命なのか
PostgreSQLのB-treeインデックスは、左から順にソートされた状態で保存されている。
例えば `(status, created_at)` という複合インデックスを作ったとしよう。このとき、インデックスの中身は以下のようになっている。
1. まず `status` でグループ化される。
2. その `status` の中で、さらに `created_at` の順に並ぶ。
もし君が「`created_at` だけを条件にして検索したい」と思っても、PostgreSQLは最初の列である `status` が不明だと、インデックスの「枝」を辿るのが難しい。これが、順序を間違えるとインデックスが効かない(あるいは効率が悪い)理由だ。
—
実践的ルール:迷ったらこの「黄金比」でいけ
現場で迷ったときは、以下の優先順位で列を並べるのが定石だ。
1. 範囲検索よりも「等価検索」を左に
`WHERE status = ‘active’ AND created_at > ‘2023-01-01’` というクエリなら、`(status, created_at)` の順にするべきだ。
`status` はピンポイントで絞り込める(等価検索)けど、`created_at` は範囲検索だよね。「より絞り込める(カーディナリティが高い)」かつ「等価検索」の列を左側に置く。これが鉄則。
2. ソート順序を考慮する
もしクエリに `ORDER BY` が含まれているなら、その列をインデックスの最後に追加することを検討してほしい。
— こんなクエリが頻出する場合
SELECT FROM orders
WHERE user_id = 123
ORDER BY created_at DESC;
この場合、`(user_id, created_at)` というインデックスがあれば、PostgreSQLはソート処理(filesort)をスキップできる。インデックス自体がすでに並んでいるから、読み込むだけで済むんだ。これは実行計画の `cost` を劇的に下げるよ。
—
やってはいけない「アンチパターン」
よくあるのが「インデックスが長すぎる」問題だ。
— 悪い例:列が多すぎる
CREATE INDEX idx_logs_all ON logs (category, severity, user_id, ip_address, created_at);
一見便利そうだけど、インデックスは更新(UPDATE/INSERT)のたびに書き換えが発生するコストがある。無駄に巨大なインデックスは、書き込み性能をガリガリ削るんだ。「そのインデックス、本当にそのクエリで使われてる?」と、`EXPLAIN ANALYZE` を使って確認する癖をつけよう。
—
先輩からのアドバイス:Explainを読め
理論も大事だけど、結局は実環境のデータでどう動くかが全て。クエリを投げるときは、必ず頭に `EXPLAIN (ANALYZE, BUFFERS)` をつけて実行してくれ。
EXPLAIN (ANALYZE, BUFFERS)
SELECT FROM orders WHERE user_id = 123 ORDER BY created_at DESC;
ここで `Index Scan` が出ているか、それとも `Seq Scan` や `Sort` が発生していないかを見るんだ。特に `Sort` が出ているなら、インデックスの順序を見直す余地があるっていうサインだよ。
—
最後に
複合インデックスは、SQLの「読み書き」を最適化するための強力な武器だ。でも、武器も使い方を間違えれば自分を傷つける。
まずは自分の書いているクエリの `WHERE` 句と `ORDER BY` を見直してみて。一番絞り込める列はどれか? ソートに使われている列はどれか? それをパズルのように組み合わせるのが、データベースエンジニアの醍醐味だよ。
もし「このクエリのインデックス、これで合ってるかな?」と悩んだら、いつでもコードを持ってきてくれ。一緒に実行計画を眺めようじゃないか。
それじゃ、今日も良いクエリライフを!
コメント