PostgreSQL: 失敗した CREATE INDEX CONCURRENTLY は INVALID なインデックスを残す
CREATE INDEX CONCURRENTLY がデッドロックや一意制約違反で途中失敗すると、インデックスは消えずに INVALID の状態で残る。クエリには使われないのに、書き込みのたびに更新コストだけかかる。マイグレーション後に pg_index.indisvalid を確認する。
どんな時に気にする?
- 本番のテーブルにロックを取らずインデックスを足すため、マイグレーションで
CONCURRENTLYを使っている - マイグレーションツールが失敗を握りつぶした / リトライで
already existsになった - インデックスを足したのに実行計画で使われない、書き込みだけ遅くなった
確認と対処
- 無効なインデックスを探す
sql
SELECT indexrelid::regclass AS index, indrelid::regclass AS "table" FROM pg_index WHERE NOT indisvalid;psqlの\d テーブル名でも、該当インデックスにINVALIDと表示される。 - 原因(重複データ、ロック競合など)を直してから作り直す
sql
DROP INDEX CONCURRENTLY IF EXISTS idx_orders_user_id; CREATE INDEX CONCURRENTLY idx_orders_user_id ON orders (user_id);PostgreSQL 12 以上なら
REINDEX INDEX CONCURRENTLY idx_orders_user_id;で作り直すこともできる。 - マイグレーションの最後に 1. のクエリを流し、1 行でも返ったら失敗扱いにすると見逃さない。
注意点
- 一意インデックス(
CREATE UNIQUE INDEX CONCURRENTLY)の場合、INVALID のままでも一意性のチェックが働き続けることがあり、重複データの挿入がエラーになる。早めに消すか作り直す。 CONCURRENTLYはトランザクションの中では実行できない。マイグレーションツールが 1 ファイルを 1 トランザクションで包む場合は、その設定を外す必要がある。REINDEX CONCURRENTLY自体が失敗すると、_ccnewで終わる名前の INVALID インデックスが残ることがある。同じクエリで見つけて消す。
出典
- @amitsinha__07 · 2026-10-19
- PostgreSQL ドキュメント(CREATE INDEX: Building Indexes Concurrently): postgresql.org