pg_repack -- PostgreSQLデータベースのテーブルを最小限のロックで再編成します

pg_repack はPostgreSQLの拡張の一つで、肥大化したテーブルやインデックスを再編成し、さらに指定したインデックスにしたがってレコードを並び替えることができます。 PostgreSQLの CLUSTERVACUUM FULL コマンドと違って、pg_repackは処理の間対象テーブルへの排他ロックを保持し続けないため、オンライン中に動作させることができます。 pg_repackはCLUSTERコマンドを直接実行するのと同じくらいの性能で起動することができて効率的です。

pg_repack は pg_reorg からフォークしたプロジェクトです。 バグ報告や開発情報については project page を参照してください。

pg_repackでは再編成する方法として次のものが選択できます。

  • オンラインCLUSTER (cluster index順にレコードを並び替える)
  • 指定したカラムでレコードを並び替える
  • オンラインVACUUM FULL (レコードのすきまを詰める)
  • 指定したテーブルのインデックスだけを再構築、もしくは再配置する

注意:

  • DBのスーパーユーザだけがpg_repackを実行できます
  • 対象となるテーブルは主キー、もしくはNOT NULL制約を持つカラムへのユニーク制約をもつインデックスが存在している必要があります

動作環境

PostgreSQL バージョン
PostgreSQL 9.1, 9.2, 9.3, 9.4, 9.5, 9.6
ディスク
テーブル全体の再編成を行うには、対象となるテーブルと付属するインデックスのおよそ2倍のサイズのディスク空き容量が必要です。例えば、テーブルとインデックスを合わせたサイズが1GBの場合、2GBのディスク領域が必要となります。

ダウンロード

pg_repackは、PGXNのWebサイトから ダウンロード できます。 ダウンロードしたアーカイブを展開し、以下の手順にしたがって インストール してください。

もしくは、 PGXN Client を使ってダウンロードからコンパイル、インストールすることもできます。:

$ pgxn install pg_repack

利用可能なオプションについては pgxn install コマンドのドキュメント を参照してください。

インストール

Unix やLinux上では、pg_repackは make コマンドでビルドすることができます。 その際、PostgreSQLの拡張向けの構築基盤であるPGXSが自動で利用されます。 ビルトに当たっては、事前にPostgreSQLの開発パッケージ (postgresql-devel, etc.)をインストールしておく必要があるかもしれません。 そして、 pg_config コマンドが存在するディレクトリが $PATH に追加されている必要があります。

その上で、以下のコマンドを実行します。:

$ cd pg_repack
$ make
$ sudo make install

Windows OS上ではMicrosoft Visual C++ 2010を利用してビルドすることができます。 msvc ディレクトリ配下にプロジェクトファイルがあります。

インストールを行った後、pg_repack エクステンションを対象のデータベースに登録します。 PostgreSQL 9.1以上のバージョンでは、以下のコマンドで実施できます。

$ psql -c "CREATE EXTENSION pg_repack" -d your_database

それ以前のPostgreSQLバージョンの場合は、 $SHAREDIR/contrib/pg_repack.sql スクリプトを対象とするデータベースに対して実施します。 $SHAREDIRpg_config --sharedir コマンドを実行することで確認できます。

$ psql -f "$(pg_config --sharedir)/contrib/pg_repack.sql" -d your_database

pg_repackの登録を削除するには、PostgreSQL 9.1以上のバージョンでは、DROP EXTENSION pg_repack を対象データベースに実行します。それ以前のPostgreSQLバージョンの場合は、 $SHAREDIR/contrib/uninstall_pg_repack.sql スクリプトを実行するか、 repack スキーマを削除します。

pg_repackもしくはpg_reorgの古いバージョンからのアップグレードを行うには、古いバージョンをデータベースから上記の手順で削除し、新しいバージョンを登録します。

利用方法

pg_repack [OPTION]... [DBNAME]

OPTIONには以下のものが指定できます。

固有オプション:
-a, --all すべてのデータベースに対して実行します
-t, --table=TABLE
 指定したテーブルに対して実行します
-I, --parent-table=TABLE
 指定したテーブルとそれを継承する全ての子テーブルに対して実行します
-c, --schema=SCHEMA
 指定したスキーマに存在するテーブル全てに対して実行します
-s, --tablespace=TBLSPC
 指定したテーブル空間に再編成後のテーブルを配置します
-S, --moveidx -s/--tablespaceで指定したテーブル空間に再編成対象のテーブルに付与されたインデックスも配置します
-o, --order-by=COLUMNS
 指定したカラムの値順に再編成します
-n, --no-order オンラインVACUUM FULL相当の処理を行います
-N, --dry-run 実際の処理は行わず、メッセージのみだけ出力します
-j, --jobs=NUM 指定した並列度で処理を行います
-i, --index=INDEX
 指定したインデックスのみ再編成します
-x, --only-indexes
 指定したテーブルに付与されたインデックスだけを再編成します
-T, --wait-timeout=SECS
 ロック競合している他のトランザクションをキャンセルするまで待機する時間を指定します
-D, --no-kill-backend
 タイムアウト時に他のバックエンドをキャンセルしません
-Z, --no-analyze
 再編成後にANALYZEを行いません
-k, --no-superuser-check
 接続ユーザがスーパーユーザかどうかのチェックを行いません
接続オプション:
-d, --dbname=DBNAME
 接続する対象のデータベースを指定します
-h, --host=HOSTNAME
 接続する対象のホスト名、もしくはUNIXソケットドメインディレクトリを指定します
-p, --port=PORT
 接続する対象のデータベース・サーバのポート番号を指定します
-U, --username=USERNAME
 接続するユーザ名を指定します
-w, --no-password
 パスワードの入力表示を無効化します
-W, --password パスワード入力表示を強制的に表示します
一般オプション:
-e, --echo サーバに送信するSQLを表示します
-E, --elevel=LEVEL
 ログ出力レベルを指定します
--help 使用方法を表示します

再編成オプション

-a, --all
データベースクラスタのすべてのデータベースを再編成します。pg_repackのエクステンションがインストールされていないデータベースはスキップされます。
-t TABLE, --table=TABLE
指定したテーブルのみを再編成します。 -t オプションを複数同時に使用することで、複数のテーブルを指定することができます。このオプションを指定しない限り、対象のデータベースに存在するすべてのテーブルを再編成します。
-I TABLE, --parent-table=TABLE
指定したテーブルとその子テーブルのみを再編成します。 -I オプションを複数同時に使用することで、複数の親テーブルを指定することができます。
-c, --schema
指定したスキーマに存在するテーブルを再編成します。 -c オプションを複数同時に指定することで、複数のスキーマを指定することができます。 --tablespace オプションと同時に使用することで、特定のスキーマのテーブルを別のテーブル空間に移動する利用例が挙げられます。
-o COLUMNS [,...], --order-by=COLUMNS [,...]
指定したカラムの値を用いてオンラインCLUSTER処理を実行します。
-n, --no-order
オンラインVACUUM FULL処理を実行します。バージョン1.2から、クラスタキーのないテーブルに対してはこれがデフォルトの挙動になっています。
-N, --dry-run
実際の処理は実行せずに、実施する内容についてのメッセージだけを出力します。
-j, --jobs
指定した数だけ追加でPostgreSQLへのコネクションを作成し、それらのコネクションを使って並列でインデックス作成処理を行います。並列でのインデックス作成は、テーブル全体を再編成する場合にのみ有効です。 --index--only-indexes オプションとは同時に利用できません。PostgreSQLサーバのCPUコア数およびディスクI/Oに余裕がある場合には、このオプションを利用することでpg_repackの処理を高速化するための有力な手段になりえます。
-s TBLSPC, --tablespace=TBLSPC
再編成したテーブルを指定したテーブル空間に移動します。即ち、 ALTER TABLE ... SET TABLESPACE 相当の処理をオンラインで実施します。 --moveidx オプションを併用しない限り、再編成したテーブルのインデックスは元のテーブル空間に残されます。
-S, --moveidx
--tablespace オプションと併用することで、再編成したテーブルのインデックスも指定したテーブル空間に移動します。
-i, --index
指定したインデックスのみを再編成します。 -i オプションを複数同時に指定することで、複数のインデックスを指定することができます。 --tablespace オプションと同時に使用することで、特定のスキーマのテーブルを別のテーブル空間に移動する利用例が挙げられます。
-x, --only-indexes
--table または --parent-table オプションと併用することで、指定したテーブルのインデックスのみを再編成します。
-T SECS, --wait-timeout=SECS
pg_repackは再編成の完了直前に排他ロックを利用します。このオプションは、このロック取得時に何秒間pg_repackが取得を待機するかを指定します。指定した時間経ってもロックが取得できないかつ、no-kill-backendオプションが指定されていない場合、pg_repackは競合するクエリを強制的にキャンセルさせます。PostgreSQL 8.4以上のバージョンを利用している場合、指定した時間の2倍以上経ってもロックが取得できない場合、pg_repackは競合するクエリを実行しているPostgreSQLバックエンドプロセスをpg_terminate_backend()関数により強制的に停止させます。このオプションのデフォルトは60秒です。
-D, --no-kill-backend
--wait-timeoutオプションで指定された時間が経過してもロックが取得できない場合、競合するクエリをキャンセルする代わりに対象テーブルの再編成をスキップします。
-Z, --no-analyze
再編成終了後にANALYZEを行うことを無効にします。デフォルトでは再編成完了後に統計情報を更新するためANALYZEを実行します。
-k, --no-superuser-check
接続ユーザがスーパーユーザかどうかのチェックを行いません。これは、非スーパーユーザのみが利用できる環境でpg_repackを使用するときに有用です。

接続オプション

PostgreSQLサーバに接続するためのオプションです。 --all オプションと同時に --dbname--table--parent-table を利用することはできません。

-a, --all
すべてのデータベースを再編成します。
-d DBNAME, --dbname=DBNAME
指定したデータベースのみを再編成します。このオプションや -a ( --all )オプションを指定しなかった場合、環境変数PGDATABASEで指定されたデータベースを再編成します。PGDATABASEも指定されていない場合、接続に利用するユーザ名と同じ名称のデータベースを再編成します。
-h HOSTNAME, --host=HOSTNAME
指定したホスト名を持つサーバ上のPostgreSQLに接続します。指定した値が / で始まる場合、Unixドメインソケットが配置されたディレクトリと解釈して接続します。
-p PORT, --port=PORT
指定したポート番号でPostgreSQLサーバに接続します。
-U USERNAME, --username=USERNAME
指定したユーザ名でPostgreSQLサーバに接続します。
-w, --no-password
接続時にパスワード入力プロンプトを表示されないようにします。もし接続先のPostgreSQLサーバがパスワード認証を要求していて、パスワードが``.pgpass``ファイルなどの手段で取得できない場合、pg_repackは接続に失敗します。このオプションはパスワード入力なしで接続できるユーザを用いたバッチ処理やスクリプトにて利用します。
-W, --password
接続時にパスワード入力プロンプトを強制的に表示します。 サーバがパスワード認証を要求する場合、そもそも自動的にパスワード入力が促されるため、このオプションが重要になることはありません。 しかし、サーバにパスワードが必要かどうかを判断するための接続試行を無駄に行います。 こうした余計な接続試行を防ぎたいのであれば、このオプションが利用してください。

一般オプション

-e, --echo
サーバに送信するSQLを表示します。
-E LEVEL, --elevel=LEVEL
ログ出力レベルを設定します。 DEBUG, INFO. NOTICE, WARNING, ERROR, LOG, FATAL, PANIC から選択できます。デフォルトは INFO です。
--help
利用方法についての説明を表示します。
--version
バージョン情報を表示します。

環境変数

PGDATABASE, PGHOST, PGPORT, PGUSER
接続パラメータのデフォルト値として利用されます。

  また、このユーティリティは、他のほとんどの PostgreSQL ユーティリティと同様、libpq でサポートされる環境変数を使用します。詳細については、 環境変数 の項目を参照してください。

利用例

以下のコマンドは、 test データベースのクラスタ可能なテーブル全てに対してオンラインCLUSTERを行い、その他のテーブルに対してオンラインVACUUM FULLを行います。:

$ pg_repack test

test データベースの foo テーブルと bar テーブルに対してオンラインVACUUM FULLを実行するには、以下のようにします。

$ pg_repack --no-order --table foo --table bar test

foo テーブルのインデックス全てをテーブル空間 tbs に移動するには、以下のようにします。

$ pg_repack -d test --table foo --only-indexes --tablespace tbs

インデックス idx をテーブル空間 tbs に移動するには、以下のようにします。

$ pg_repack -d test --index idx --tablespace tbs

トラブルシューティング

pg_repackが失敗した場合、エラーメッセージが表示されます。 エラーの原因について以下に列記します。

FATALエラーが発生した場合、手動でクリーンアップを行う必要があります。 クリーンアップするには、pg_repackをデータベースから一度削除し、再度登録するだけです。 PostgreSQL 9.1以降では、 DROP EXTENSION pg_repack CASCADE をエラーが起きた データベースで実行し、続いて CREATE EXTENSION pg_repack を実行します。 これより古いバージョンの場合、 $SHAREDIR/contrib/uninstall_pg_repack.sql スクリプトをエラーが起きたデータベースに対して実行し、その後 $SHAREDIR/contrib/pg_repack.sql を同様に実行します。

INFO: database "db" skipped: pg_repack VER is not installed in the database

--all オプション指定時に、pg_repackがインストールされていない データベースに対して表示されます。

該当のデータベースに対してpg_repackをインストールしてください。

ERROR: pg_repack VER is not installed in the database

--dbname オプション指定時に、指定したデータベースにpg_repackが インストールされていない場合に表示されます。

該当のデータベースに対してpg_repackをインストールしてください。

ERROR: program 'pg_repack V1' does not match database library 'pg_repack V2'

There is a mismatch between the pg_repack binary and the database library (.so or .dll).

データベースに登録されたpg_repackがバージョン2系であるのに、クライアント側 コマンドのpg_repackのバージョンが1系である場合に表示されます。 $PATH に誤ったpg_repackのバイナリを指定していたり、接続先のデータベースが 間違っている可能性があります。pg_repackプログラムがインストールされた ディレクトリとデータベースを確認してください。それらが適切である場合、 pg_repackを再インストールしてください。

ERROR: extension 'pg_repack V1' required, found extension 'pg_repack V2'
クライアント側のpg_repackがバージョン1系であるのに、データベース側に 登録されたpg_repackがバージョン2系の場合に表示されます。 当該データベースからpg_repackを削除し、 インストール に従って 再登録してください。
ERROR: relation "table" must have a primary key or not-null unique keys
対象のテーブルが主キーもしくはNOT NULLなユニーク制約を持っていない場合に表示されます。 主キーもしくはユニーク制約を定義してください。
ERROR: query failed: ERROR: column "col" does not exist
対象のテーブルが --order-by オプションで指定したカラムを持っていない場合に表示されます。 存在しているカラムを指定してください。
WARNING: the table "tbl" already has a trigger called repack_trigger
以前に実行したが何らかの理由で中断したか、あるいは失敗したpg_repackコマンドにより、 対象テーブルにpg_repackが利用するトリガが残存している場合に表示されます。 pg_repackを一度削除して、再度登録することで、こうした一時オブジェクトを削除できます。 インストール を参照してください。
ERROR: Another pg_repack command may be running on the table. Please try again
同じテーブルに複数のpg_repackが同時に実行されている場合に表示されます。 これはデッドロックを引き起こす可能性があるため、片方のpg_repackが終了するのを 待って再度実行してください。

WARNING: Cannot create index "schema"."index_xxxxx", already exists DETAIL: An invalid index may have been left behind by a previous pg_repack on the table which was interrupted. Please use DROP INDEX "schema"."index_xxxxx" to remove this index and try again.

以前に実行したが何らかの理由で中断したか、あるいは失敗したpg_repackコマンドにより、 pg_repackが利用する一時的なインデックスが残存している場合に表示されます。 DROP INDEXコマンドにより該当のインデックスを削除して、pg_repackを再実行してください。

制約

pg_repackには以下の制約があります。

一時テーブル

pg_repackは一時テーブルは再編成できません。

GiSTインデックス

pg_repackはGiSTインデックスを使ってテーブルを再編成することはできません。

DDLコマンド

pg_repackを実行している間、VACUUMもしくはANALYZE以外のDDLコマンドを対象の テーブルに対して実行することはできません。何故ならば、pg_repackは ACCESS SHAREロックを対象テーブルに対して保持しつづけるからです。

バージョン1.1.8もしくはそれ以前のバージョンを使っている場合、あらゆるDDL コマンドをpg_repackが走っているテーブルに対して実行することができません。 大抵はpg_repackが失敗してロールバックが適切に行われますが、古いバージョンでは いくつかのケースでデータ不整合を引き起こす可能性があります。

動作詳細

テーブル再編成

テーブル全体を再編成する場合、pg_repackは以下のように動作します:

  1. 対象のテーブルに対して実行される変更を記録するためのログテーブルを作成します
  2. 対象のテーブルに、INSERT、UPDATE、DELETEが行われた際にログテーブルに変更内容を記録するトリガを追加します
  3. 対象テーブルに含まれるレコードを元に、新しいテーブルを指定した編成順でレコードを並ばせながら作成します
  4. 新しいテーブルに対してインデックスを作成します
  5. 再編成中に行われた元のテーブルに対する変更内容をログテーブルから取り出し、新しいテーブルに反映します
  6. システムカタログを更新し、元のテーブルと新しいテーブルを入れ替えます。インデックスやトーストテーブルも入れ替えます
  7. 元のテーブルを削除します

pg_repackは上の手順の中で、始めの1.と2.の時点、および最後の6.と7.の時点で対象のテーブルに対する ACCESS EXCLUSIVEロックを取得します。その他のステップでは、ACCESS SHAREロックを必要とするだけなので、 元のテーブルに対するINSERT, UPDATE, DELETE操作は通常通りに実行されます。

インデックスのみの再編成

インデックスのみ再編成する場合、pg_repackは以下のように動作します:

  1. 元のインデックス定義に添って、新しいインデックスをCONCURRENTLYオプションを利用して作成します
  2. システムカタログを更新し、元のインデックスと新しいインデックスを入れ替えます
  3. 元のインデックスを削除します

インデックス作成のCONCURRENTLYオプションにはいくつかの注意点があります。 詳細は、 PostgreSQLドキュメント を参照してください。

リリースノート

  • pg_repack 1.4.3
    • CVE-2018-1058を利用した攻撃の可能性を修正しました (issue #168)
    • PostgreSQLでのCVE-2018-1058の修正により"unexpected index definition"エラーが発生する事象を修正しました (issue #169)
    • 最近のUbuntuパッケージでビルドが失敗する事象を修正しました (issue #179)
  • pg_repack 1.4.2
    • PostgreSQL 10をサポートしました (issue #120)
    • エラー「DROP INDEX CONCURRENTLY cannot run inside a transaction block」が発生する事象を修正しました (issue #129)
  • pg_repack 1.4.1
    • 壊れていた --order-by オプションを修正しました (issue #138)
  • pg_repack 1.4
    • PostgreSQL 9.6をサポートしました
    • INSERT CONFLICT を同時実行した際の問題を解決するために、 AFTER トリガを使うようにしました(issue #106)
    • --no-kill-backend オプションを追加しました (issue #108)
    • --no-superuser-check オプションを追加しました (issue #114)
    • --exclude-extension オプションを追加しました (#97)
    • --parent-table オプションを追加しました(#117)
    • TOASTテーブルの格納オプションを再編成後のテーブルに再設定するようにしました (issue #10)
    • 列の格納タイプを再編成後のテーブルに再設定するようにしました (issue #94)
  • pg_repack 1.3.4
    • 元テーブルを削除する前に排他ロックを取得するようにしました(#81)
    • Unlogged Tableを再編成対象から外すようにしました (#71)
  • pg_repack 1.3.3
    • PostgreSQL 9.5をサポートしました
    • pg_repackが中断されたときにデッドロックが発生する可能性を修正しました (issue #55)
    • --help または --version オプションを指定した実行したときの終了コードを修正しました
    • 日本語のユーザマニュアルを追加しました
  • pg_repack 1.3.2
    • pg_repackが中断されたときに一時オブジェクトを削除するようにしました
    • pg_statsinfoと同時にロードされている時にクラッシュする可能性を修正しました
  • pg_repack 1.3.1
    • PostgreSQL 9.4をサポートしました
  • pg_repack 1.3
    • 特定のスキーマのみを再編成対象とする --schema オプションを追加しました ( issue #20)
    • ドライランのための --dry-run オプションを追加しました (issue #21)
    • 勧告的ロックを取得する際のOIDの扱いを修正しました (issue #30)
    • 再編成予定のテーブルに対して別のセッションたロックを保持している場合にデッドロックが起きないように修正しました (issue #32)
    • 一度に複数のDELETE操作をsql_popで取り扱う際の性能を改善しました
    • 常に高負荷の更新が行われているテーブルに対する再編成処理が終わらない事象が起きないように修正しました
  • pg_repack 1.2
    • PostgreSQL 9.3をサポートしました
    • オンラインSET TABLESPACE文に相当する処理を行うためのオプション --tablespace, --moveidx を追加しました
    • 特定のインデックスのみを再編成するためのオプション --index を追加しました
    • 特定のテーブルのインデックスをまとめて再編成するオプション --only-indexes を追加しました
    • 並列実行のためのオプション --jobs を追加しました
    • クラスタキーを持たないテーブルに対してVACUUM FULL相当の処理を行うために --no-order オプションを明示的に指定しなくてもよいようにしました (pg_repack issue #6)
    • 他のデータベースにおけるロックを待たないようにしました (pg_repack issue #11)
    • バグ修正: DESC, NULL FIRST/LAST, COLLATEを持つインデックスキーを正しく取り扱えるように修正しました (pg_repack issue #3)
    • 同時に行われる削除操作によってデータ破壊が起こる可能性があったため修正しました (pg_repack issue #23)
    • 出力メッセージとエラーメッセージを改善しました
  • pg_repack 1.1.8
    • PostgreSQL 9.2をサポートしました
    • PostgreSQL 9.1およびそれ以降のバージョンでCREATE EXTENSIONによるインストールが行えるようにしました
    • 他のトランザクションの終了を待っていることをユーザに通知するようにしました (pg_reorg issue #5)
    • バグ修正: ストリーミングレプリケーション構成において、新たにマスタに昇格したサーバ上で動作するように修正しました (pg_reorg issue #1)
    • バグ修正: pg_repackとSlony 2.0/2.1が競合しないように修正しました (pg_reorg issue #4)
    • バグ修正: カラム名を適切にエスケープするように修正しました (pg_reorg issue #6)
    • バグ修正: invalidなインデックスを再編成の対象としたり、クラスタキーとして扱うことがないように修正しました (pg_reorg issue #9)
    • バグ修正: 部分インデックスを主キーとして選択しないように修正しました (pg_reorg issue #22)
  • pg_reorg 1.1.7 (2011-08-07)
    • バグ修正: 削除されたカラムを持つテーブルを再編成した際に、そのテーブルに対するビューや関数が壊れないように修正しました
    • PostgreSQL 9.1および9.2devをサポートしました (EXTENSIONはまだサポートしていません)

関連項目