

# pg\$1repack 拡張機能を使用して、テーブルやインデックスの膨張を抑制する
<a name="Appendix.PostgreSQL.CommonDBATasks.pg_repack"></a>

`pg_repack` 拡張機能を使用して、`VACUUM FULL` の代わりとしてテーブルやインデックスの肥大化を取り除くことができます。このエクステンションは、RDS for PostgreSQL のバージョン 9.6.3 以降でサポートされています。`pg_repack` 拡張機能の詳細については、[GitHub プロジェクトのドキュメント](https://reorg.github.io/pg_repack/)をご覧ください。

`VACUUM FULL` とは異なり、`pg_repack` 拡張機能では、次の場合にテーブルの再構築オペレーション中に短期間だけ排他的ロック (AccessExclusiveLock) が必要です。
+ ログテーブルの初回作成 – 次の例に示すように、データの初回コピー中に発生した変更を記録するログテーブルが作成されます。

  ```
  postgres=>\dt+ repack.log_*
  List of relations
  -[ RECORD 1 ]-+----------
  Schema        | repack
  Name          | log_16490
  Type          | table
  Owner         | postgres
  Persistence   | permanent
  Access method | heap
  Size          | 65 MB
  Description   |
  ```
+ 最終スワップアンドドロップフェーズ。

再構築オペレーションの残りの部分で必要なのは、元のテーブルから新しいテーブルに行をコピーするための `ACCESS SHARE` ロックのみです。これにより、INSERT、UPDATE、DELETE オペレーションを通常どおりに進めることができます。

## 推奨事項
<a name="Appendix.PostgreSQL.CommonDBATasks.pg_repack.Recommen"></a>

次の推奨事項は、`pg_repack` 拡張機能を使用してテーブルとインデックスの肥大化を取り除く場合に適用されます。
+ 業務時間外または他のデータベースアクティビティのパフォーマンスへの影響を最小限に抑えるために、メンテナンスウィンドウで再パックを実行します。
+ 再構築アクティビティ中にブロッキングセッションを注意深くモニタリングし、`pg_repack` をブロックする可能性のあるアクティビティが元のテーブルにないことを確認します。特に、元のテーブルで排他的ロックが必要なときは、最後のスワップアンドドロップフェーズ中にアクティビティがないことを確認します。詳細については、「[クエリをブロックしているものの特定](https://repost.aws/knowledge-center/rds-aurora-postgresql-query-blocked)」を参照してください。

  ブロッキングセッションが表示された場合は、慎重に検討した後、次のコマンドを使用してセッションを終了できます。これは、`pg_repack` の継続によって再構築を完了するのに役立ちます。

  ```
  SELECT pg_terminate_backend(pid);
  ```
+ トランザクション率が非常に高いシステムで `pg_repack's` ログテーブルから蓄積された変更を適用すると、適用プロセスが変更の速度に対して遅れる可能性があります。このような場合、`pg_repack` は適用プロセスを完了できません。詳細については、「[再パック中の新しいテーブルのモニタリング](#Appendix.PostgreSQL.CommonDBATasks.pg_repack.Monitoring)」を参照してください。インデックスが著しく肥大化している場合、代替の解決策は、インデックスのみの再パックを実行することです。これにより、VACUUM のインデックスクリーンアップサイクルをより速く完了させることもできます。

  PostgreSQL バージョン 12 の手動 VACUUM を使用してインデックスのクリーンアップフェーズをスキップできます。また、PostgreSQL バージョン 14 の緊急自動バキューム中は自動的にスキップされます。これにより、VACUUM はインデックスの肥大化を取り除くことなくより迅速に完了します。これは、循環 VACUUM の防止などの緊急時にのみ使用されます。詳細については、Amazon Aurora ユーザーガイドの「[インデックスの肥大化の回避](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.diag-table-ind-bloat.html#AuroraPostgreSQL.diag-table-ind-bloat.AvoidinginIndexes)」を参照してください。

## 前提条件
<a name="Appendix.PostgreSQL.CommonDBATasks.pg_repack.Prereq"></a>
+ テーブルには、PRIMARY KEY 制約または null 以外の UNIQUE 制約が必要です。
+ 拡張機能のバージョンは、クライアントとサーバーの両方で同じである必要があります。
+ RDS インスタンスに、肥大化がないテーブルの合計サイズ以上の `FreeStorageSpace` があることを確認します。例として、TOAST とインデックスを含むテーブルの合計サイズが 2TB で、テーブルの肥大化の合計が 1TB であるとします。必須の `FreeStorageSpace` は、次の計算によって返される値よりも大きくなければなりません。

   `2TB (Table size)` - `1TB (Table bloat)` = `1TB`

  次のクエリを使用してテーブルの合計サイズを確認し、`pgstattuple` を使用して肥大化を導き出すことができます。詳細については、Amazon Aurora ユーザーガイドの「[テーブルとインデックスの肥大化の診断](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.diag-table-ind-bloat.html)」を参照してください。

  ```
  SELECT pg_size_pretty(pg_total_relation_size('table_name')) AS total_table_size;
  ```

  このスペースは、アクティビティの完了後に再利用されます。
+ RDS インスタンスに再パックオペレーションを処理するのに十分なコンピューティング容量と IO 容量があることを確認します。パフォーマンスのバランスを最適化するために、インスタンスクラスをスケールアップすることを検討してください。

**`pg_repack` 拡張機能を使用するには**

1. 次のコマンドを実行して、RDS for PostgreSQL DB インスタンスに `pg_repack` エクステンションをインストールします。

   ```
   CREATE EXTENSION pg_repack;
   ```

1. 次のコマンドを実行して、`pg_repack` によって作成されたテンポラリログテーブルへの書き込みアクセス権を付与します。

   ```
   ALTER DEFAULT PRIVILEGES IN SCHEMA repack GRANT INSERT ON TABLES TO PUBLIC;
   ALTER DEFAULT PRIVILEGES IN SCHEMA repack GRANT USAGE, SELECT ON SEQUENCES TO PUBLIC;
   ```

1. `pg_repack` クライアントユーティリティを使用してデータベースに接続します。`rds_superuser` 権限を持つアカウントを使用します。例として、`rds_test` ロールに `rds_superuser` 権限があるとします。次の構文は、`postgres` データベース内のすべてのテーブルインデックスを含む完全なテーブルに対して `pg_repack` を実行します。

   ```
   pg_repack -h db-instance-name.111122223333.aws-region.rds.amazonaws.com -U rds_test -k postgres
   ```
**注記**  
-k オプションを使用して接続する必要があります。-a オプションはサポートされていません。

   `pg_repack` クライアントからのレスポンスにより、再パッケージされる DB インスタンスのテーブルに関する情報が提供されます。

   ```
   INFO: repacking table "pgbench_tellers"
   INFO: repacking table "pgbench_accounts"
   INFO: repacking table "pgbench_branches"
   ```

1. 次の構文は、`postgres` データベース内のインデックスを含む単一のテーブル `orders` を再パックします。

   ```
   pg_repack -h db-instance-name.111122223333.aws-region.rds.amazonaws.com -U rds_test --table orders -k postgres
   ```

   次の構文では、`postgres` データベース内の `orders` テーブルのインデックスのみを再パックします。

   ```
   pg_repack -h db-instance-name.111122223333.aws-region.rds.amazonaws.com -U rds_test --table orders --only-indexes -k postgres
   ```

## 再パック中の新しいテーブルのモニタリング
<a name="Appendix.PostgreSQL.CommonDBATasks.pg_repack.Monitoring"></a>
+ データベースのサイズは、再パックのスワップアンドドロップフェーズまで、テーブルの合計サイズから肥大化を引いた数だけ増加します。データベースサイズの増加率をモニタリングし、再パックの速度を計算して、最初のデータ転送の完了にかかる時間を概算で見積もることができます。

  例えば、テーブルの合計サイズを 2TB、データベースのサイズを 4TB、テーブルの合計肥大化を 1TB とします。再パックオペレーションの最後に計算によって返されるデータベースの合計サイズ値は次のとおりです。

   `2TB (Table size)` \$1 `4 TB (Database size)` - `1TB (Table bloat)` = `5TB`

  再パックオペレーションの速度を概算で見積もるには、2 つの時点の間の増加率をバイト単位でサンプリングします。増加率が 1GB の場合、最初のテーブル構築オペレーションが完了するまでに 1000 分または 16.6 時間かかることがあります。最初のテーブル構築に加えて、`pg_repack` は蓄積された変更を適用する必要があります。所要時間は、進行中の変更と蓄積された変更の適用速度によって異なります。
**注記**  
`pgstattuple` 拡張機能を使用して、テーブルの肥大化を計算できます。詳細については、「[pgstattuple](https://www.postgresql.org/docs/current/pgstattuple.html)」を参照してください。
+ 再パックスキーマの下の `pg_repack's` ログテーブルの行数は、最初のロード後に新しいテーブルに適用される保留中の変更の量を表します。

  `pg_stat_all_tables` の `pg_repack's` ログテーブルをチェックして、新しいテーブルに適用される変更をモニタリングできます。`pg_stat_all_tables.n_live_tup` は、新しいテーブルに適用される保留中のレコードの数を示します。詳細については、「[pg\$1stat\$1all\$1tables](https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-ALL-TABLES-VIEW)」を参照してください。

  ```
  postgres=>SELECT relname,n_live_tup FROM pg_stat_all_tables WHERE schemaname = 'repack' AND relname ILIKE '%log%';
          
  -[ RECORD 1 ]---------
  relname    | log_16490
  n_live_tup | 2000000
  ```
+ `pg_stat_statements` 拡張機能を使用して、再パックオペレーションの各ステップにかかる時間を調べることができます。これは、本番環境で同じ再パックオペレーションを適用する準備に役立ちます。出力をさらに拡張するように `LIMIT` 句を調整できます。

  ```
  postgres=>SELECT
       SUBSTR(query, 1, 100) query,
       round((round(total_exec_time::numeric, 6) / 1000 / 60),4) total_exec_time_in_minutes
   FROM
       pg_stat_statements
   WHERE
       query ILIKE '%repack%'
   ORDER BY
       total_exec_time DESC LIMIT 5;
          
   query                                                                 | total_exec_time_in_minutes
  -----------------------------------------------------------------------+----------------------------
   CREATE UNIQUE INDEX index_16493 ON repack.table_16490 USING btree (a) |                     6.8627
   INSERT INTO repack.table_16490 SELECT a FROM ONLY public.t1           |                     6.4150
   SELECT repack.repack_apply($1, $2, $3, $4, $5, $6)                    |                     0.5395
   SELECT repack.repack_drop($1, $2)                                     |                     0.0004
   SELECT repack.repack_swap($1)                                         |                     0.0004
  (5 rows)
  ```

再パックは完全にアウトオブプレースオペレーションであるため、元のテーブルは影響を受けず、元のテーブルの復元を必要とする予期しない課題は予想されません。再パックが予期せず失敗した場合は、エラーの原因を調べて解決する必要があります。

問題が解決したら、テーブルが存在するデータベースに `pg_repack` 拡張機能を削除して再作成し、`pg_repack` ステップを再試行してください。さらに、コンピューティングリソースの可用性とテーブルの同時アクセシビリティは、再パックオペレーションをタイムリーに完了させる上で重要な役割を果たします。