★PostgreSQLカンファレンス2021 11月12日開催/チケット販売中★
他のバージョンの文書 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

REINDEX

REINDEX — インデックスを再構築する

概要

REINDEX [ ( VERBOSE ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } [ CONCURRENTLY ] name

説明

REINDEXは、インデックスのテーブルに保存されたデータを使用してインデックスを再構築し、古いインデックスのコピーと置き換えます。 以下にREINDEXが使用される状況を示します。

  • インデックスが破損してしまい、有効なデータがなくなった場合です。 理論的には決して起こらないはずですが、実際には、ソフトウェアのバグやハードウェアの障害によりインデックスが破損することがあります。 REINDEXはこの修復手段を提供します。

  • インデックスが膨張状態、つまり、多くの空、もしくは、ほとんど空のページを持つ状態になっている場合です。 この状況は、PostgreSQLのB-treeインデックスが特定の普通でないパターンでアクセスされた場合に起こり得ます。 REINDEXを使って、使用されないページを取り除いた新しいインデックス作成すると、インデックスの領域消費量を減少することができます。 詳細は24.2を参照してください。

  • インデックスの格納パラメータ(フィルファクタなど)を変更し、この変更を確実に有効にしたい場合です。

  • CONCURRENTLYオプションをつけたインデックス作成が失敗すると、このインデックスは無効として残されます。 こうしたインデックスは使用されませんが、REINDEXを使用して再作成するのが便利かもしれません。 REINDEX INDEXだけが無効なインデックスでの同時構築を実行できることに注意してください。

パラメータ

INDEX

指定したインデックスを再作成します。

TABLE

指定したテーブルの全インデックスを再作成します。 テーブルに2次的なTOASTテーブルがある場合、それについてもインデックスを再作成します。

SCHEMA

指定したスキーマのすべてのインデックスを再作成します。 このスキーマのテーブルが二次的なTOASTテーブルを持っている場合は、そのインデックスも再作成されます。 共有システムカタログのインデックスも処理されます。 この構文のREINDEXはトランザクションブロックの内側で実行することはできません。

DATABASE

現在のデータベースのすべてのインデックスを再作成します。 共有システムカタログのインデックスも処理されます。 この構文のREINDEXをトランザクションブロック内で実行することはできません。

SYSTEM

現在のデータベースのシステムカタログに対するすべてのインデックスを再作成します。 共有システムカタログのインデックスも含みます。 ユーザテーブルのインデックスは処理されません。 この構文のREINDEXをトランザクションブロック内で実行することはできません。

name

インデックスを再作成するインデックス、テーブル、データベースの名前です。 インデックスとテーブルはスキーマ修飾可能です。 現状では、REINDEX DATABASEREINDEX SYSTEMは現在のデータベースのインデックスのみを再作成することができます。 そのため、このパラメータは現在のデータベース名と一致する必要があります。

CONCURRENTLY

このオプションが使われると、PostgreSQLは、そのテーブルで同時実行される挿入、更新、削除を妨げるようなロックを取得せずにインデックスを再構築します。一方、標準のインデックス再構築は終了するまでテーブルの書き込みをロックします(読み込みはロックしません)。 このオプションを使用する場合に注意すべき点がいくつかあります—インデックスを同時に再構築を参照してください。

一時テーブルに対してはREINDEXは常に同時再作成ではありません。他のセッションはアクセスできませんし、同時でないインデックス再作成の方がより安価だからです。

VERBOSE

各インデックスが再作成されるときに、進捗レポートを表示します。

注釈

ユーザテーブル上の特定のインデックスに破損の疑いがある場合、REINDEX INDEXを使ってそのインデックスを再構築することもできますし、REINDEX TABLEを使ってそのテーブルのすべてのインデックスを再構築することもできます。

システムテーブルのインデックスの破損を復旧する場合の手順はより複雑になります。 この場合、システムによって破損の可能性があるインデックス自体が使用されないようにすることが重要です (実際は、このようなケースでは、破損したインデックスに依存していたため、サーバプロセスが起動時に強制終了してしまう可能性があります)。 安全に復旧させるには、システムカタログ検索時のインデックスの使用を禁止する-Pオプションを使用してサーバを起動しなければなりません。

考えられる方法の1つは次の方法です。まず、サーバを停止して、コマンドラインから-Pオプションを指定してシングルユーザ状態のPostgreSQLサーバを起動します。 そして、再構成する範囲に応じて、REINDEX DATABASEREINDEX SYSTEMREINDEX TABLE、または、REINDEX INDEXコマンドを発行します。 範囲が不明な場合は、REINDEX SYSTEMを使用して、そのデータベースの全てのシステムインデックスを再構成してください。 その後、シングルユーザ状態のサーバセッションを停止して、通常のサーバを再起動します。 シングルユーザ状態のサーバインタフェースの操作方法についての詳細は、postgresマニュアルページを参照してください。

その他、コマンドラインで-Pを指定して通常のサーバセッションを起動することもできます。 具体的な方法は、クライアントによって異なります。 しかし、libpqベースのクライアントであれば、クライアントを起動する前に環境変数PGOPTIONS-Pに設定すれば実現できます。 この方法では他のクライアントを締め出す必要はありませんが、修復が終わるまで破損したデータベースへの他のユーザの接続を防止する方が良いことに注意してください。

REINDEXは、インデックスの中身を1から作り直すという点では、インデックスを削除してから再作成する処理と似ています。 しかし、ロックに関しては異なります。 REINDEXはインデックスの元となるテーブルの書き込みをロックしますが、読み込みはロックしません。 また、処理中のインデックスに対する排他ロックを取得するので、そのインデックスを使用する読み込みはブロックされます。 一方、DROP INDEXは瞬間的に元となるテーブルの排他ロックを取得するので、書き込みも読み込みもブロックされます。 その後に行うCREATE INDEXでは書き込みのみをロックし、読み込みはロックしません。 インデックスは存在しないので、インデックスを使用する読み込みは発生しません。 したがって、読み込みがブロックされることはありませんが、コストが高いシーケンシャルスキャンの使用を強制されることになります。

単一インデックスまたは単一テーブルのインデックス再作成を行うには、そのインデックスまたはテーブルの所有者でなければなりません。 スキーマまたはデータベースに対するインデックス再作成を行うには、そのスキーマまたはデータベースの所有者でなければなりません。 したがって、非スーパーユーザが他のユーザが所有するテーブルのインデックスを再作成することができる場合があることに注意してください。 しかし、特別な例外として、REINDEX DATABASEREINDEX SCHEMAREINDEX SYSTEMが非スーパーユーザにより発行された時には、そのユーザがカタログを所有している場合(そのようなことは通常はありません)を除いて、共有カタログのインデックスは飛ばされます。 もちろん、スーパーユーザは常にすべてのインデックス再作成を行うことができます。

パーティションテーブルやパーティションインデックスのインデックス再作成はサポートされていません。 その代わり、個々のパーティションで別々にインデックスを再作成できます。

インデックスを同時に再構築

インデックスの再構築は、通常のデータベース操作を妨げることがあります。 通常、PostgreSQLはインデックスが再構築されるテーブルへの書き込みをロックし、一度のテーブル走査で全インデックスの構築を実行します。 他のトランザクションはテーブルを読み込めますが、そのテーブルで行を挿入、更新、削除しようとするとインデックスの再構築が終わるまでブロックされます。 実行中の運用状態のデータベースシステムの場合、これは重大な影響を与えるかもしれません。 非常に大規模なテーブルに対するインデックス作成は何時間もかかることがあり得ます。また小規模なテーブルであっても、インデックス再構築により、運用状態のシステムとしては受け入れられないほど長い時間、書き込みロックがかかる可能性があります。

PostgreSQLは最小限の書き込みロックでのインデックス再構築をサポートしています。 REINDEXCONCURRENTLYオプションをつけることでこの方式が行われます。 このオプションを使うと、PostgreSQLは再構築が必要な各インデックスに関してテーブルを2回走査しなければなりません。さらに、潜在的にそのインデックスを使用する可能性がある、実行中のすべてのトランザクションが終わるまで待機しなければなりません。 したがって、この方式は通常のインデックス再構築よりも総作業時間がかかり、また、インデックスを修正する可能性のある終了していないトランザクションが待つ必要がありますので、完了するまでの時間が非常に長くなります。 しかし、インデックス再構築中に通常の操作を続けることができますので、この方式は運用環境でのインデックス再構築に有用です。 もちろん、インデックス再構築によりCPUやメモリ、入出力に余分に負荷がかかりますので、他の操作が低速になる可能性があります。

同時実行再インデックスは以下のような段階で行なわれます。 各段階は分離したトランザクション内で実行されます。 複数のインデックスを再構築する場合、次の段階に移る前にすべてのインデックスに対して各段階が繰り返されます。

  1. カタログpg_indexに新しく一時的なインデックス定義が追加されます。 この定義は古いインデックスを置き換えるのに使われます。 処理中は、再インデックスされるインデックスと関連するテーブルに対して、セッションレベルでのSHARE UPDATE EXCLUSIVEロックを取得します。スキーマが修正されないようにするためです。

  2. インデックス構築の第1段階は新しいインデックスそれぞれに対して行なわれます。 インデックスが一度構築されれば、挿入の準備ができたということで、そのフラグpg_index.indisreadytrueに切り替わります。構築を実行したトランザクションが終わった後で、他のセッションから見えるようになります。 この過程は各インデックスに対して分離したトランザクションで行なわれます。

  3. 次に、第1段階実行中に追加されたタプルを追加する第2段階が行なわれます。 この過程は各インデックスに対して分離したトランザクションで行なわれます。

  4. インデックスを参照する制約は、すべて新しいインデックス定義を参照するよう変更され、インデックスの名前が変更されます。 この時点で、pg_index.indisvalidは新しいインデックスに対してはtrueに切り替えられ、古いものに対してはfalseに切り替えられます。そして、古いインデックスを参照するセッションをすべて無効にするためキャッシュの無効化が行なわれます。

  5. 古いインデックスを参照している可能性のある実行中の問い合わせが完了するのをまってから、新しいタプルが挿入されないように古いインデックスはpg_index.indisreadyfalseに切り替えられます。

  6. 古いインデックスが削除されます。 インデックスやテーブルに対するSHARE UPDATE EXCLUSIVEセッションロックは解放されます。

インデックスの再構築中に、一意性インデックスでの一意性違反などの問題が発生したら、REINDEXコマンドは失敗しますが、既に存在しているものに加えて無効な新しいインデックスを残します。 このインデックスは不完全な可能性がありますので、問い合わせの目的では無視されます。しかし、更新のオーバーヘッドは消費し続けるでしょう。 psql \dコマンドはそのようなインデックスをINVALIDと報告します。

postgres=# \d tab
       Table "public.tab"
 Column |  Type   | Modifiers
--------+---------+-----------
 col    | integer |
Indexes:
    "idx" btree (col)
    "idx_ccnew" btree (col) INVALID

そのような場合のお勧めの回復法は無効なインデックスを削除して、再度REINDEX CONCURRENTLYを実行してみることです。 その処理の間に作成された同時実行インデックスは、接尾辞ccnewで終わる名前です。または、削除に失敗した古いインデックス定義ならccoldです。 無効なTOASTインデックスを含む無効なインデックスはDROP INDEXで削除できます。

通常のインデックス構築は、同じテーブルでの他の通常のインデックス構築を許しますが、同時実行インデックス構築は1つだけが一度に1つのテーブルでできます。 どちらの場合でも、その間のそのテーブルでの他の種類のスキーマ修正は認められていません。 もう一つの違いは、通常のREINDEX TABLEREINDEX INDEXコマンドはトランザクションブロックの内側で実行できますが、REINDEX CONCURRENTLYはできないことです。

システムカタログは同時実行で再インデックスできませんので、REINDEX SYSTEMCONCURRENTLYをサポートしません。

さらに、排他制約に対するインデックスは同時実行で再インデックスできません。 このコマンドでそのようなインデックスの名前が直接指定されたら、エラーが起きます。 排他制約インデックスのあるテーブルやテータベースが同時実行で再インデックスされる場合、そのインデックスは飛ばされます。 (そのようなインデックスをCONCURRENTLYオプションなしで再インデックスすることは可能です。)

単一のインデックスを再構築します。

REINDEX INDEX my_index;

テーブルmy_table上のすべてのインデックスを再構築します。

REINDEX TABLE my_table;

システムインデックスが有効かどうかを確認することなく、あるデータベース内の全てのインデックスを再構築します。

$ export PGOPTIONS="-P"
$ psql broken_db
...
broken_db=> REINDEX DATABASE broken_db;
broken_db=> \q

再インデックスの進行中に、関連するリレーションの読み書きをブロックすることなく、テーブルに対するインデックスを再構築します。

REINDEX TABLE CONCURRENTLY my_broken_table;

互換性

標準SQLにはREINDEXはありません。

関連項目

CREATE INDEX, DROP INDEX, reindexdb