【実務・中級編】 部分インデックス (Partial Indexes) – PostgreSQL

データベースの「断捨離」、できてますか? PostgreSQLの部分インデックス活用術

やあ。最近、データベースのパフォーマンスチューニングで頭を抱えていないかな?

エンジニアとして経験を積んでくると、とりあえず「よく検索するカラムにはインデックスを貼る」というクセがつく。それは間違っていない。でも、テーブルが数千万行、数億行と肥大化してきたとき、そのインデックスが逆にシステムの首を絞めていることに気づくことがあるんだ。

今日は、そんな状況を劇的に改善する、PostgreSQLの「隠し玉」のような機能、部分インデックス(Partial Indexes)の話をしようと思う。

—

なぜ「全部」インデックスする必要があるのか?

まず、自問自答してみてほしい。「本当にそのテーブルのすべての行に対してインデックスが必要?」と。

例えば、よくある「メール配信システム」を想像してくれ。`messages`テーブルがあって、ここには数千万件のデータが溜まっている。そのうち、まだ送信されていない「未送信(`status = ‘pending’`)」のメッセージだけを抽出してバッチ処理を回すケースはよくあるよね。

— よくある(そして少し残念な)インデックス
CREATE INDEX idx_messages_status ON messages(status);

これだと、すでに送信済みの数千万件の「不要なデータ」までインデックスツリーに含まれてしまう。インデックスのサイズは巨大になり、メモリ(Shared Buffers)を圧迫し、さらには書き込み時のインデックス更新コストも無駄にかさむ。

ここで「部分インデックス」の出番だ。

—

部分インデックスの書き方

やり方は驚くほどシンプルだ。`CREATE INDEX`文に`WHERE`句を加えるだけ。

CREATE INDEX idx_messages_pending
ON messages(created_at)
WHERE status = ‘pending’;

これだけで、PostgreSQLは「`status`が`pending`である行」だけを抜き出してインデックスツリーを構築してくれる。

このインデックスのメリットは3つある。

1. 劇的な軽量化: インデックスサイズが桁違いに小さくなる。メモリに乗りやすくなるから、検索速度が跳ね上がる。
2. 更新コストの削減: `status`が`completed`になったとき、このインデックスは無視される。つまり、完了済みデータの更新時にインデックスを書き換えるオーバーヘッドが発生しない。
3. 統計情報の精度向上: 不要なデータが混じらない分、オプティマイザが適切な実行計画を選びやすくなる。

—

実務で「これ使える!」と感じるシーン3選

僕が現場でよく使う、部分インデックスが輝く具体的なパターンを紹介するよ。

1. 「論理削除」の無視

`deleted_at IS NULL`で生きているデータだけを検索するケースは多いはず。

CREATE INDEX idx_users_active_email
ON users(email)
WHERE deleted_at IS NULL;

これで、ログイン認証などのクエリが驚くほど速くなる。

2. 特定のフラグが立っている例外処理

例えば「エラーが発生したログだけを抽出したい」といったケース。

CREATE INDEX idx_logs_error_only
ON logs(created_at)
WHERE severity >= 500;

普段は正常なログがほとんどで、エラーログは稀という場合に、インデックスを極限まで小さくできる。

3. ユニーク制約の「条件付き」適用

これが一番強力かもしれない。例えば、「有効なユーザーはメールアドレスを一人一つしか持てないが、退会済みユーザーは重複してもいい」という場合。

CREATE UNIQUE INDEX idx_unique_active_email
ON users(email)
WHERE deleted_at IS NULL;

アプリ側で「退会済みだから重複OK」といった複雑なロジックを組む必要がなくなる。DBレベルで担保できるのは精神衛生上とても良いよね。

—

注意点:魔法の杖ではない

もちろん、どんな時でも使えばいいというわけじゃない。

  • クエリとWHERE句が一致しないと使われない: 当たり前だけど、`WHERE status = ‘pending’`で検索しないクエリには、このインデックスは効かない。
  • プランナの判断: あまりにインデックスの対象行が多すぎると(例えば全行の9割がターゲットの場合)、普通のインデックスの方が効率的なこともある。

「とりあえず全部貼る」という大雑把な設計から卒業して、「本当に必要なデータだけをインデックスする」という職人気質な設計にシフトしてみよう。

データベースは、正直だ。君がインデックスを整理すれば、必ずパフォーマンスという形で恩返しをしてくれる。もし今日、肥大化したテーブルで重いクエリに悩んでいるなら、ぜひ一度この部分インデックスを試してみてくれ。

それじゃあ、また現場で会おう。良いチューニングを!

コメント

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