「とりあえずインデックス貼っとけ」はもう卒業。PostgreSQLのインデックスを断捨離する技術
「クエリが遅い? じゃあインデックスを追加しよう」
エンジニアなら一度は経験する、いわゆる「インデックスのお守り」作業。でも、そのインデックス、本当にまだ必要ですか? 運用が長くなるにつれて、テーブルには「かつては光り輝いていたけれど、今は誰にも使われないインデックス」が溜まっていきがちです。
これ、ただのストレージの無駄遣いじゃありません。書き込み(INSERT/UPDATE)のたびにインデックスの更新コストが走り、DB全体のパフォーマンスを地味に、でも確実に蝕んでいく「負債」なんです。
今日は、現場で即戦力になる「不要なインデックスを特定して、安全に削除する技術」について、少し踏み込んで話をしましょう。
—
インデックスの利用状況は「鏡」に映す
PostgreSQLには、インデックスがどれくらい使われているかを教えてくれる便利なビューがあります。それが `pg_stat_user_indexes` です。
まずは、自分のDBでどのインデックスが「サボっているか」を確認してみましょう。
SELECT
relname AS table_name,
indexrelname AS index_name,
idx_scan AS number_of_scans
FROM
pg_stat_user_indexes
WHERE
idx_scan = 0
AND indexrelname NOT LIKE ‘%_pkey’ — 主キーは触らないのが吉
ORDER BY
relname;
このクエリを叩いたとき、`idx_scan = 0` のインデックスが大量に出てきたら要注意。「一度もスキャンされていない=なくても同じ」という可能性が高いです。
—
削除前の「確信」を持つためのステップ
ただ、ここで一つ注意点。`idx_scan = 0` だからといって、即座に `DROP INDEX` するのは危険です。
なぜなら、そのインデックスは「四半期に一度しか走らない集計クエリ」で使われているかもしれないし、あるいは「深夜のバッチ処理」でしか日の目を見ないものかもしれないからです。
1. 運用期間を考慮する
最低でも、一つの業務サイクル(月次処理など)をカバーする期間は統計情報を蓄積しましょう。本番環境なら、最低1ヶ月は様子を見るのが「大人の対応」です。
2. 「見えない」だけで使われている可能性を考える
実は、PostgreSQLの統計情報は DB 再起動や統計情報の再計算でリセットされることがあります。`pg_stat_user_indexes` を見るときは、`pg_stat_user_tables` の `last_analyze` や `last_autovacuum` の時刻も併せて見て、「統計がちゃんと取れている期間か?」を確認してください。
—
実践:安全にインデックスを「断捨離」する
さて、数ヶ月様子を見ても `idx_scan` がゼロ。これなら削除の対象として有力です。しかし、いきなり削除して万が一クエリが爆速で遅くなったら……と不安ですよね。
そんな時は、「インデックスを無効にする」というテクニックが使えます。
— いきなりDROPせず、まずは名前を変えておく
ALTER INDEX index_name RENAME TO index_name_unused;
— その後、インデックスを無効にする(利用不可にする)
UPDATE pg_index SET indisvalid = false WHERE indexrelid = ‘index_name_unused’::regclass;
こうしておけば、もし「あ、やっぱり必要だった!」という緊急事態が起きても、すぐに `indisvalid = true` に戻すだけで復旧できます。数日間様子を見て、アプリケーションからエラーが上がってこなければ、晴れて `DROP INDEX` です。
—
最後のアドバイス:インデックスは「最小限」が一番美しい
「インデックスはあればあるほど速くなる」という幻想は捨てましょう。
- Writeヘビーなテーブルほど、インデックスは足かせになります。
- カバリングインデックスなど、本当に必要なインデックスを洗練させるほうが、結果としてメモリ効率も上がり、DB全体が軽快に動くようになります。
インデックスの整理は、いわば庭の手入れと同じです。伸び放題の枝を剪定することで、木(データベース)はより強く、健康に育ちます。
今日の帰り道、ぜひ自分の担当しているテーブルの `idx_scan` を覗いてみてください。意外な「隠れ負債」が見つかるかもしれませんよ。
それでは、また次回の記事でお会いしましょう!
コメント