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

複合インデックスの「順番」、適当に決めてない?現場で差がつく設計の極意

やあ。データベースのパフォーマンスチューニングで頭を抱えている諸君、今日もクエリと格闘しているかな?

最近、コードレビューをしていてよく見かけるのが「とりあえずインデックスを貼っておけば速くなるだろう」という考え方だ。特に複合インデックス(複数列インデックス)を設計するとき、列の並び順をなんとなく決めてしまっているケースが非常に多い。

正直に言おう。そのインデックス、半分くらいのケースでは宝の持ち腐れになっているかもしれないぞ。

今日は、PostgreSQLにおける複合インデックスの「左端一致の原則」と、実務で絶対に外してはいけない設計のコツについて、少し深掘りして話そうと思う。

—

「左端一致」という鉄の掟

まず基本の確認だ。PostgreSQLで複合インデックス `(col_a, col_b)` を作成した場合、DBは「`col_a` でソートし、その中でさらに `col_b` でソートする」という辞書のような構造を作る。

ここで重要なのが「左端一致(Leftmost Prefix)の原則」だ。

インデックスが効くのは、インデックスの定義順の左側から順番に条件が指定されている時だけなんだ。

  • `WHERE col_a = 1 AND col_b = 2` → 完璧に効く
  • `WHERE col_a = 1` → 効く(左端が含まれているから)
  • `WHERE col_b = 2` → ほぼ効かない(左端の `col_a` が抜けているから、B-treeの枝を辿れない)

このルールを無視して「よく検索される列だから」と適当に並べると、インデックスがスキャンされず、結局フルスキャン(Seq Scan)が発生してサービスが重くなる。これが現場で一番多い「インデックスの無駄遣い」だ。

—

実践:どう並べるのが正解か?

じゃあ、具体的にどんな基準で列を並べるべきか。実務では以下の3つの視点を持ってほしい。

1. 範囲検索よりも「等価条件」を左に持ってくる

これが最も重要だ。等価(`=`) の列を左に、範囲(`>`, `<`, `BETWEEN`)の列を右に置くのが鉄則。 なぜなら、範囲検索を使ってしまうと、それ以降の列のインデックスが効果を失ってしまうからだ。 -- 悪い例: 範囲検索が左にある CREATE INDEX idx_bad ON orders (created_at, status); -- WHERE created_at > ‘2023-01-01’ AND status = ‘COMPLETED’
— この場合、statusの絞り込みにはインデックスが十分に活用されない可能性がある

— 良い例: 等価条件を左にする
CREATE INDEX idx_good ON orders (status, created_at);
— WHERE status = ‘COMPLETED’ AND created_at > ‘2023-01-01’
— これなら、statusで絞り込んだ後、その範囲内でcreated_atを高速に検索できる

2. カーディナリティ(値の重複度)を意識する

よくある誤解が「カーディナリティ(値の種類)が高い列を左に置け」というもの。確かに一理あるが、最近のPostgreSQLのオプティマイザは優秀だ。それよりも、「アプリケーションで検索条件として使われる頻度」を最優先すべきだ。

どれだけカーディナリティが高くても、その列が `WHERE` 句に一度も登場しないなら、インデックスの左端に置く意味はない。

3. 複合インデックスの「再利用性」を考える

一つのクエリのためにインデックスを作るのは、メモリの無駄だ。
例えば、以下の2つのクエリが頻繁に走るなら、インデックスを一つに統合できる。

  • `WHERE user_id = ? AND status = ?`
  • `WHERE user_id = ?`

この場合、`(user_id, status)` というインデックスを作れば、両方のクエリでインデックスが使われる。左端に `user_id` を置くことで、インデックスの恩恵を最大限に引き出せるわけだ。

—

最後に:エンジニアとしての心構え

最後に一つだけアドバイスしておこう。「インデックスは作れば作るほど、書き込み(INSERT/UPDATE)が重くなる」ということだ。

検索を速くするためにインデックスを増やしすぎると、今度はデータ更新のたびにインデックスの再構築が走り、アプリケーション全体のレスポンスが悪化する。

「本当にこの検索は頻繁に行われるのか?」「このインデックスは他のクエリでも使えないか?」を常に自問自答してほしい。`EXPLAIN ANALYZE` を叩いて、実際に `Index Scan` が発生しているかを自分の目で確かめるのが、一流のエンジニアへの近道だ。

さて、そろそろコーヒーが冷めてしまったかな。また次の現場で会おう。何か疑問があれば、いつでも聞いてくれ。

コメント

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