REINDEX — インデックスを再構築する
REINDEX [ (option
[, ...] ) ] { INDEX | TABLE | SCHEMA } [ CONCURRENTLY ]name
REINDEX [ (option
[, ...] ) ] { DATABASE | SYSTEM } [ CONCURRENTLY ] [name
] ここでoption
は以下の一つです。 CONCURRENTLY [boolean
] TABLESPACEnew_tablespace
VERBOSE [boolean
]
REINDEX
は、インデックスのテーブルに保存されたデータを使用してインデックスを再構築し、古いインデックスのコピーと置き換えます。
以下にREINDEX
が使用される状況を示します。
インデックスが破損してしまい、有効なデータがなくなった場合です。
理論的には決して起こらないはずですが、実際には、ソフトウェアのバグやハードウェアの障害によりインデックスが破損することがあります。
REINDEX
はこの修復手段を提供します。
インデックスが「膨張状態」、つまり、多くの空、もしくは、ほとんど空のページを持つ状態になっている場合です。
この状況は、PostgreSQLのB-treeインデックスが特定の普通でないパターンでアクセスされた場合に起こり得ます。
REINDEX
を使って、使用されないページを取り除いた新しいインデックス作成すると、インデックスの領域消費量を減少することができます。
詳細は25.2を参照してください。
インデックスの格納パラメータ(フィルファクタなど)を変更し、この変更を確実に有効にしたい場合です。
CONCURRENTLY
オプションをつけたインデックス作成が失敗すると、このインデックスは「無効」として残されます。
こうしたインデックスは使用されませんが、REINDEX
を使用して再作成するのが便利かもしれません。
REINDEX INDEX
だけが無効なインデックスでの同時構築を実行できることに注意してください。
INDEX
指定したインデックスを再作成します。
この構文のREINDEX
は、パーティションインデックスと使われる場合にはトランザクションブロック内で実行できません。
TABLE
指定したテーブルの全インデックスを再作成します。
テーブルに2次的な「TOAST」テーブルがある場合、それについてもインデックスを再作成します。
この構文のREINDEX
は、パーティションテーブルと使われる場合にはトランザクションブロック内で実行できません。
SCHEMA
指定したスキーマのすべてのインデックスを再作成します。
このスキーマのテーブルが二次的な「TOAST」テーブルを持っている場合は、そのインデックスも再作成されます。
共有システムカタログのインデックスも処理されます。
この構文のREINDEX
はトランザクションブロック内で実行できません。
DATABASE
システムカタログを除く、現在のデータベースのすべてのインデックスを再作成します。
システムカタログのインデックスは処理されません
この構文のREINDEX
をトランザクションブロック内で実行できません。
SYSTEM
現在のデータベースのシステムカタログに対するすべてのインデックスを再作成します。
共有システムカタログのインデックスも含みます。
ユーザテーブルのインデックスは処理されません。
この構文のREINDEX
をトランザクションブロック内で実行できません。
name
インデックスを再作成するインデックス、テーブル、データベースの名前です。
インデックスとテーブルの名前はスキーマ修飾可能です。
現状では、REINDEX DATABASE
とREINDEX SYSTEM
は現在のデータベースのインデックスのみを再作成することができます。
このパラメータは省略可能で、現在のデータベース名と一致する必要があります。
CONCURRENTLY
このオプションが使われると、PostgreSQLは、そのテーブルで同時実行される挿入、更新、削除を妨げるようなロックを取得せずにインデックスを再構築します。一方、標準のインデックス再構築は終了するまでテーブルの書き込みをロックします(読み込みはロックしません)。 このオプションを使用する場合に注意すべき点がいくつかあります—下記のインデックスを同時に再構築を参照してください。
一時テーブルに対してはREINDEX
は常に同時再作成ではありません。他のセッションはアクセスできませんし、同時でないインデックス再作成の方がより安価だからです。
TABLESPACE
新しいテーブル空間でインデックスを再構築することを指定します。
VERBOSE
各インデックスが再作成されるときに、進捗レポートを表示します。
boolean
選択したオプションをオンにするかオフにするか指定します。
オプションを有効にするにはTRUE
、ON
または1
と、無効にするにはFALSE
、OFF
または0
と書くことができます。
boolean
値は省略することもでき、その場合にはTRUE
と仮定されます。
new_tablespace
インデックスが再構築されるテーブル空間です。
ユーザテーブル上の特定のインデックスに破損の疑いがある場合、REINDEX INDEX
を使ってそのインデックスを再構築することもできますし、REINDEX TABLE
を使ってそのテーブルのすべてのインデックスを再構築することもできます。
システムテーブルのインデックスの破損を復旧する場合の手順はより複雑になります。
この場合、システムによって破損の可能性があるインデックス自体が使用されないようにすることが重要です
(実際は、このようなケースでは、破損したインデックスに依存していたため、サーバプロセスが起動時に強制終了してしまう可能性があります)。
安全に復旧させるには、システムカタログ検索時のインデックスの使用を禁止する-P
オプションを使用してサーバを起動しなければなりません。
考えられる方法の1つは次の方法です。まず、サーバを停止して、コマンドラインから-P
オプションを指定してシングルユーザ状態のPostgreSQLサーバを起動します。
そして、再構成する範囲に応じて、REINDEX DATABASE
、REINDEX SYSTEM
、REINDEX TABLE
、または、REINDEX INDEX
コマンドを発行します。
範囲が不明な場合は、REINDEX SYSTEM
を使用して、そのデータベースの全てのシステムインデックスを再構成してください。
その後、シングルユーザ状態のサーバセッションを停止して、通常のサーバを再起動します。
シングルユーザ状態のサーバインタフェースの操作方法についての詳細は、postgresマニュアルページを参照してください。
その他、コマンドラインで-P
を指定して通常のサーバセッションを起動することもできます。
具体的な方法は、クライアントによって異なります。
しかし、libpqベースのクライアントであれば、クライアントを起動する前に環境変数PGOPTIONS
を-P
に設定すれば実現できます。
この方法では他のクライアントを締め出す必要はありませんが、修復が終わるまで破損したデータベースへの他のユーザの接続を防止する方が良いことに注意してください。
REINDEX
は、インデックスの中身を1から作り直すという点では、インデックスを削除してから再作成する処理と似ています。
しかし、ロックに関しては異なります。
REINDEX
はインデックスの元となるテーブルの書き込みをロックしますが、読み込みはロックしません。
また、処理中のインデックスに対するACCESS EXCLUSIVE
ロックを取得するので、そのインデックスを使用する読み込みはブロックされます。
特に、問い合わせプランナは、問い合わせに関わらずテーブルの各インデックスでACCESS SHARE
ロックを取得しようとしますので、REINDEX
は、プランがキャッシュされていて、まさにこのインデックスを使わないプリペアド問い合わせを除く問い合わせをすべて実質的にブロックします。
一方、DROP INDEX
は瞬間的に元となるテーブルのACCESS EXCLUSIVE
ロックを取得するので、書き込みも読み込みもブロックされます。
その後に行うCREATE INDEX
では書き込みのみをロックし、読み込みはロックしません。
インデックスは存在しないので、インデックスを使用する読み込みは発生しません。
したがって、読み込みがブロックされることはありませんが、コストが高いシーケンシャルスキャンの使用を強制されることになるかもしれません。
単一インデックスまたは単一テーブルのインデックス再作成を行うには、そのインデックスまたはテーブルの所有者でなければなりません。
スキーマまたはデータベースに対するインデックス再作成を行うには、そのスキーマまたはデータベースの所有者でなければなりません。
したがって、非スーパーユーザが他のユーザが所有するテーブルのインデックスを再作成できることに特に注意してください。
しかし、特別な例外として、REINDEX DATABASE
、REINDEX SCHEMA
、REINDEX SYSTEM
が非スーパーユーザにより発行された時には、そのユーザがカタログを所有している場合(そのようなことは通常はありません)を除いて、共有カタログのインデックスは飛ばされます。
もちろん、スーパーユーザは常にすべてのインデックス再作成を行うことができます。
パーティションインデックスやパーティションテーブルのインデックス再作成は、それぞれREINDEX INDEX
、REINDEX TABLE
でサポートされています。
指定されたパーティションリレーションの個々のパーティションが、別々のトランザクションでインデックスを再作成されます。
パーティションテーブルやパーティションインデックスに対して操作する場合、このコマンドをトランザクションブロック内では使えません。
パーティションインデックスやパーティションテーブルでTABLESPACE
句を使ってREINDEX
を行なう場合、リーフパーティションのテーブル空間参照のみが更新されます。
パーティションインデックスは更新されませんので、アタッチされた新しいパーティションが新しいテーブル空間を継承するように、それぞれALTER TABLE ONLY
を使うことをお勧めします。
失敗した場合、インデックスはすべて新しいテーブル空間へと移動したわけではないかもしれません。
コマンドを再実行すればリーフパーティションはすべて再構築され、先ほどは処理されなかったインデックスが新しいテーブル空間へと移動します。
SCHEMA
、DATABASE
、SYSTEM
がTABLESPACE
と一緒に使われた場合、システムリレーションは飛ばされ、WARNING
が1度出ます。
TOASTテーブルのインデックスは再構築されますが、新しいテーブル空間には移動しません。
インデックスの再構築は、通常のデータベース操作を妨げることがあります。 通常、PostgreSQLはインデックスが再構築されるテーブルへの書き込みをロックし、一度のテーブル走査で全インデックスの構築を実行します。 他のトランザクションはテーブルを読み込めますが、そのテーブルで行を挿入、更新、削除しようとするとインデックスの再構築が終わるまでブロックされます。 実行中の運用状態のデータベースシステムの場合、これは重大な影響を与えるかもしれません。 非常に大規模なテーブルに対するインデックス作成は何時間もかかることがあり得ます。また小規模なテーブルであっても、インデックス再構築により、運用状態のシステムとしては受け入れられないほど長い時間、書き込みロックがかかる可能性があります。
PostgreSQLは最小限の書き込みロックでのインデックス再構築をサポートしています。
REINDEX
にCONCURRENTLY
オプションをつけることでこの方式が行われます。
このオプションを使うと、PostgreSQLは再構築が必要な各インデックスに関してテーブルを2回走査しなければなりません。さらに、潜在的にそのインデックスを使用する可能性がある、実行中のすべてのトランザクションが終わるまで待機しなければなりません。
したがって、この方式は通常のインデックス再構築よりも総作業時間がかかり、また、インデックスを修正する可能性のある終了していないトランザクションが待つ必要がありますので、完了するまでの時間が非常に長くなります。
しかし、インデックス再構築中に通常の操作を続けることができますので、この方式は運用環境でのインデックス再構築に有用です。
もちろん、インデックス再構築によりCPUやメモリ、入出力に余分に負荷がかかりますので、他の操作が低速になる可能性があります。
同時実行再インデックスは以下のような段階で行なわれます。 各段階は分離したトランザクション内で実行されます。 複数のインデックスを再構築する場合、次の段階に移る前にすべてのインデックスに対して各段階が繰り返されます。
カタログpg_index
に新しく一時的なインデックス定義が追加されます。
この定義は古いインデックスを置き換えるのに使われます。
処理中は、再インデックスされるインデックスと関連するテーブルに対して、セッションレベルでのSHARE UPDATE EXCLUSIVE
ロックを取得します。スキーマが修正されないようにするためです。
インデックス構築の第1段階は新しいインデックスそれぞれに対して行なわれます。
インデックスが一度構築されれば、挿入の準備ができたということで、そのフラグpg_index.indisready
は「true」に切り替わります。構築を実行したトランザクションが終わった後で、他のセッションから見えるようになります。
この過程は各インデックスに対して分離したトランザクションで行なわれます。
次に、第1段階実行中に追加されたタプルを追加する第2段階が行なわれます。 この過程は各インデックスに対して分離したトランザクションで行なわれます。
インデックスを参照する制約は、すべて新しいインデックス定義を参照するよう変更され、インデックスの名前が変更されます。
この時点で、pg_index.indisvalid
は新しいインデックスに対しては「true」に切り替えられ、古いものに対しては「false」に切り替えられます。そして、古いインデックスを参照するセッションをすべて無効にするためキャッシュの無効化が行なわれます。
古いインデックスを参照している可能性のある実行中の問い合わせが完了するのをまってから、新しいタプルが挿入されないように古いインデックスはpg_index.indisready
が「false」に切り替えられます。
古いインデックスが削除されます。
インデックスやテーブルに対する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
INVALID
と印づけられたインデックスに接尾辞ccnew
がついている場合、それは同時実行操作中に作られた一時的なインデックスに対応します。お勧めの回復法はDROP INDEX
を使ってそれを削除して、再度REINDEX CONCURRENTLY
を試みることです。
無効なインデックスにその代わりに接尾辞ccold
がついている場合、それは削除できなかった元のインデックスに対応します。
正式な再構築は成功していますので、お勧めの回復法は単に前記のインデックスを削除することです。
通常のインデックス構築は、同じテーブルでの他の通常のインデックス構築を許しますが、同時実行インデックス構築は1つだけが一度に1つのテーブルでできます。
どちらの場合でも、その間のそのテーブルでの他の種類のスキーマ修正は認められていません。
もう一つの違いは、通常のREINDEX TABLE
やREINDEX INDEX
コマンドはトランザクションブロックの内側で実行できますが、REINDEX CONCURRENTLY
はできないことです。
他の時間のかかるトランザクションと同じく、あるテーブルに対するREINDEX
は、その他のテーブルに対する同時実行中のVACUUM
によりどのタプルが削除できるかに影響します。
システムカタログは同時実行で再インデックスできませんので、REINDEX SYSTEM
はCONCURRENTLY
をサポートしません。
さらに、排他制約に対するインデックスは同時実行で再インデックスできません。
このコマンドでそのようなインデックスの名前が直接指定されたら、エラーが起きます。
排他制約インデックスのあるテーブルやデータベースが同時実行で再インデックスされる場合、そのインデックスはスキップされます。
(そのようなインデックスをCONCURRENTLY
オプションなしで再インデックスすることは可能です。)
REINDEX
を実行している各バックエンドはその進捗をpg_stat_progress_create_index
ビューで報告します。
詳細は28.4.4を参照してください。
単一のインデックスを再構築します。
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
はありません。