テック技報最新号へ →
コード

PostgreSQL: 失敗した CREATE INDEX CONCURRENTLY は INVALID なインデックスを残す

CREATE INDEX CONCURRENTLY がデッドロックや一意制約違反で途中失敗すると、インデックスは消えずに INVALID の状態で残る。クエリには使われないのに、書き込みのたびに更新コストだけかかる。マイグレーション後に pg_index.indisvalid を確認する。

@amitsinha__07 · 投稿 2026-10-19
#postgres#database#migration#infra

どんな時に気にする?

  • 本番のテーブルにロックを取らずインデックスを足すため、マイグレーションで CONCURRENTLY を使っている
  • マイグレーションツールが失敗を握りつぶした / リトライで already exists になった
  • インデックスを足したのに実行計画で使われない、書き込みだけ遅くなった

確認と対処

  1. 無効なインデックスを探す
    sql
    SELECT indexrelid::regclass AS index, indrelid::regclass AS "table"
    FROM pg_index
    WHERE NOT indisvalid;

    psql の \d テーブル名 でも、該当インデックスに INVALID と表示される。

  2. 原因(重複データ、ロック競合など)を直してから作り直す
    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; で作り直すこともできる。

  3. マイグレーションの最後に 1. のクエリを流し、1 行でも返ったら失敗扱いにすると見逃さない。

注意点

  • 一意インデックス(CREATE UNIQUE INDEX CONCURRENTLY)の場合、INVALID のままでも一意性のチェックが働き続けることがあり、重複データの挿入がエラーになる。早めに消すか作り直す。
  • CONCURRENTLY はトランザクションの中では実行できない。マイグレーションツールが 1 ファイルを 1 トランザクションで包む場合は、その設定を外す必要がある。
  • REINDEX CONCURRENTLY 自体が失敗すると、_ccnew で終わる名前の INVALID インデックスが残ることがある。同じクエリで見つけて消す。

出典

X の元の投稿を開く