【実務・中級編】 複合インデックスの設計と順序 – PostgreSQL

複合インデックスの「順番」、適当に決めてない?

現場でコードレビューをしていると、複合インデックス(マルチカラムインデックス)を作るときに「とりあえず使われる列を全部ぶち込んでおくか」という設計をよく見かけるんだよね。

正直に言うと、それ、半分正解で半分は「宝の持ち腐れ」だよ。

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` を見直してみて。一番絞り込める列はどれか? ソートに使われている列はどれか? それをパズルのように組み合わせるのが、データベースエンジニアの醍醐味だよ。

もし「このクエリのインデックス、これで合ってるかな?」と悩んだら、いつでもコードを持ってきてくれ。一緒に実行計画を眺めようじゃないか。

それじゃ、今日も良いクエリライフを!

コメント

タイトルとURLをコピーしました