運用トラブルを防止するVACUUMのチューニング ~ XID周回問題と性能影響を防ぐ ~
PostgreSQLインサイド

PostgreSQLには、同時実行制御においてデータの一貫性を維持するために、各レコードにトランザクションIDを付与して管理する仕組みがあります。このトランザクションIDには上限がありサイクリックに使用されるため、VACUUM機能によって古いトランザクションIDを回収し、再利用可能とする仕組み(凍結処理)があります。VACUUMはデフォルトで自動的に実施することができます。
しかし、頻繁にレコードを更新する業務などトランザクションIDが大量に発生する場合には、凍結処理が追いつかず、システム停止を伴うトランザクションIDの周回問題(以降、XID周回問題と呼びます)に発展する可能性があるため、VACCUM処理の実行多重度や実行頻度のチューニングが必要です。また、このチューニングは運用中の業務に影響を与えないよう対策する必要があります。

本記事では、PostgreSQL内部メカニズムを踏まえ、VACUUM処理の種類や使い分け、監視方法、および効果的なチューニング方法を解説します。

目次

1. XID周回問題に対するVACUUMの働き

1.1 トランザクションID(XID)とXID周回問題

トランザクションID(以降、XID)とは、トラザクションごとに自動で割り振られる識別子です。
テーブルの各行にはXIDを管理する列が存在し、挿入や更新されたときのXIDが記録されます。現在のXIDの値を基準にして、それ以前を過去のXID(可視の状態)とし、未来に実行されるトランザクションのXIDは不可視の状態として識別することで、他のトランザクションからの可視化の判断に利用されます。
XIDには32ビットの符号なし整数を使用しているため、約40億個(2の32乗個)のトランザクションが稼働すると、周回して0から割り振られます。また、現在のXIDの値を基準にして、それ以前の約20億個(XID全体の半分)を過去のXID、約20億先までを未来のXIDとして識別します。このため、現在のXIDから約20億個よりも前の古いXIDを持つレコードが存在すると、そのXIDが未来のXIDとして識別されて参照できなくなり、データの整合性が損なわれるXID周回問題が発生します。
この対処法として、PostgreSQLではVACUUMによるXIDの凍結処理が用いられます。凍結処理は、定期的に古いXIDを再利用可能とします。

XID周回問題とVACUUMによる凍結処理については、以下の記事に詳しく解説しています。

XID枯渇によるシステム停止について

PostgreSQLでは、XID周回問題でデータが不正な状態になる前にシステムを停止させられるよう、安全マージンが取られています。データベース内で一番古いXIDと現在のXIDとの差(以降age)が一定数以上になると、システムが停止するため、注意が必要です。

XID周回問題が発生するまでに残り4000万トラザクション(※)を切ると、WARNINGが出力され始めます。

WARNING: database "mydb" must be vacuumed within 39985967 transactions
HINT: To avoid a database shutdown, execute a database-wide VACUUM in "mydb".

また、XID周回問題が発生するまでに残り300万トランザクション(※)を切ると、新しいトランザクションはエラーとなります。

ERROR: database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT: Stop the postmaster and vacuum that database in single-user mode.

このエラーが発生すると、データベースレコードの更新や、リレーションを切り捨てる操作は失敗するため、対象データベースでVACUUMを実行する必要があります(※)
上記のような、XID枯渇によってトランザクションの新規発行ができなくなることを防ぐために、定期的なVACUUMによるXIDの凍結処理が推奨されます。

  • PostgreSQLのバージョンにより値や対処方法が異なります。本書ではPostgreSQL 17を例にしています。詳細はマニュアルを参照してください。

1.2 VACUUMの種類と違い

VACUUMによるXID回収の手法には、大きく以下の2つがあります。ここでは、各手法の特徴について説明します。

  • autovacuum
  • 手動VACUUM(vacuumdbコマンド、またはSQL実行)

autovacuumと手動VACUUMの違い

PostgreSQLには、VACUUMを自動で実行するためのautovacuumという機構があり、定期的にVACUUM処理が実行可能です。
一方で直接SQLを実行する、またはPostgreSQLクライアントアプリケーション(vacuumdbコマンド)を使用することで、手動でVACUUM処理を実行可能です。
autovacuumと手動VACUUMでは、自動実行の他にも異なる点がありますので、注意してください。

autovacuumと手動VACUUMの機能比較表

autovacuumには2つの実行契機があります。autovacuumではデータ肥大化を防止するために実行された場合(以下、通常autovacuum)と、XID周回問題を防止するために実行された場合(以下、周回問題防止autovacuum)で動作が異なります。

通常autovacuum 周回問題防止autovacuum 手動VACUUM
実行契機 デッドタプル数が指定値(※1)以上 XIDの最古位置から現在位置まで(age)が指定値(※2)以上 vacuumdbコマンド / SQL実行時
対象テーブルリスト デッドタプル回収が必要なテーブル(※3) デッドタプル回収 / XID凍結が必要なテーブル(※3) 実行時オプションにて指定
多重度 postgresql.confの設定に依存 vacuumdbコマンドのオプション指定、またはSQLの並列実行数
テーブルの実行順番 対象テーブルリストのテーブル定義順 指定したテーブルのサイズが大きい順
VACUUM時間要因 テーブル数、テーブルサイズ、VACUUM実行多重度、VACUUMのスリープ実行頻度
排他 テーブル単位 VACUUMが先行 ページ処理ごとにSHARE UPDATE EXCLUSIVE以上の排他を取得する業務が待機していないか確認し、待機していた場合は次のテーブルに対する処理が動作 対象テーブルのVACUUM完了までSHARE UPDATE EXCLUSIVE以上の排他を取得する業務が待機
VACUUMが待機 該当のテーブルをスキップ 先行プロセスの排他解除まで待機
ページ単位 VACUUMが先行 該当ページの操作完了まで他業務が待機
VACUUMが待機 該当のページをスキップ 先行プロセスの排他解除まで待機
運用によるユーザー負荷 ユーザーによるスケジュールは不要 ユーザーによるスケジュールが必要
DBへの負荷
  • WAL / DB領域へのIO量増(対象テーブルのデッドタプルを保有するページのみ対象とするため、ページ更新量低)
  • 実行時間の調整不可(業務の高負荷時間帯に動作しやすい)
  • IO負荷量を調整可能
  • WAL / DB領域へのIO量増(対象テーブルの全ページを対象とするため、ページ更新量高)
  • 実行時間の調整不可(業務の高負荷時間帯に動作しやすい)
  • IO負荷量を調整可能
  • WAL / DB領域へのIO量増(対象テーブルの全ページを対象とするため、ページ更新量高)
  • 実行時間 / IO負荷量を調整可能
  • ※1
    autovacuum_vacuum_thresholdとautovacuum_vacuum_scale_factorから算出される値
  • ※2
    autovacuum_freeze_max_ageの値
  • ※3
    テーブルは、PostgreSQLが「実行契機」より自動で特定

1.3 XID枯渇によるユーザー影響の事例

XID枯渇によりユーザー影響が発生した事例をご紹介します。

【発生した事例】
データベースのログに「database is not accepting commands to avoid wraparound data loss in database」のメッセージが出力され、コマンドを受け付けない状態となっていた。

【原因と対策】
毎日1億トランザクション以上発行される状態であり、かつテーブル数が多い(約20万テーブル)ためVACUUM処理に時間がかかったことが原因であり、autovacuumのみの運用では利用可能なXIDが枯渇する状況(※1)。対策として、毎日ageの高い4万テーブル(※2)を手動VACUUMする運用に変更することで解消した。

【解説】
※1  利用可能なXIDの枯渇状況を確認する方法について
利用可能なXIDの枯渇状況を確認するためには、PostgreSQLの統計情報を確認する必要があります。
データベースごとに状況を確認し、対処する必要があります。以下はSQLの実行例です。

SELECT datname, age(datfrozenxid) FROM pg_database;
     datname     |   age
-----------------+---------
 postgres        | 2571843
 db              | 2571843
 template1       | 2571843
 template0       | 2571843
 testdb          | 2571843
(5 rows)

※2  ageの高いテーブルを判断する方法について
ageの高いテーブルを判断するためには、PostgreSQLの統計情報を確認する必要があります。
テーブルごとに状況を確認し、ageが高いテーブルを判断してください。以下はSQLの実行例です。

SELECT c.oid::regclass as table_name, greatest(age(c.relfrozenxid), age(t.relfrozenxid)) as age FROM pg_class c LEFT JOIN pg_class t ON c.reltoastrelid = t.oid WHERE c.relkind IN ('r', 'm') ORDER BY age DESC;
                 table_name                 |   age
--------------------------------------------+---------
 pg_statistic                               | 2571843
 t1                                         | 2571836
 table_info                                 |    4874
 time_data                                  |     687
 tbl                                        |     619

2. VACUUM運用の監視と対策

2.1 VACUUM運用の監視項目

現在運用中のシステムにおいてXID周回問題が発生する可能性があるか確認するために、以下を監視してください。

項番 監視項目 監視方法
1 データベースごとの未凍結XID数 以下SQLを一日一回発行し、データベースごとの未凍結XID数を確認してください。
SELECT datname, age(datfrozenxid) FROM pg_database;
2 XID発行数 以下SQLを一日一回発行し、データベースで監視間隔ごとのXID発行数を確認してください。
  • PostgreSQL 12以前
  • SELECT txid_current();
  • PostgreSQL 13以降
  • SELECT pg_current_xact_id();

2.2 監視項目の評価と対策

上記で監視した項目を評価し、対策が必要か確認してください。

項番 監視項目 評価と対策
1 データベースごとの未凍結XID数 評価
age(datfrozenxid)がautovacuum_freeze_max_ageを超えた場合に、周回問題防止autovacuumが動作します。周回問題防止autovacuumによるXID回収よりもXID発行の頻度が上回る場合、未凍結XID数が蓄積する可能性があります。詳細は【補足】を参照してください。
未凍結XID数が増加傾向の場合、以下の対策を実施してください。

対策
autovacuumの回収頻度を上げるためのチューニングを検討してください。詳細は「3.1 autovacuumのチューニング」を参照してください。autovacuumのチューニングによる影響が許容できない場合、手動VACUUMの計画を検討してください。詳細は「3.2 手動VACUUMの設計とチューニング」を参照してください。
2 XID発行数 評価
想定したXID発行数よりも多い、または増加傾向にある場合に、以下の対策を実施してください。

対策
想定外の業務が動作していないか確認してください。

【補足】

未凍結XID数が蓄積するケースについて
複数の周回問題防止autovacuumが実行された区間を見ると、未凍結XID数は、周回問題防止autovacuumの動作開始と終了によって、autovacuum_freeze_max_ageの設定値を基準に増減を繰り返します。
もし未凍結XID数のピークが徐々に大きくなっているならば、減少量が不十分な可能性があります。特にautovacuum_freeze_max_age以下にならなくなった場合は、周回問題防止autovacuumが動き続ける状態となり、特に注意が必要です。

未凍結のXID数が蓄積するケース

3. VACUUM運用のチューニング

以下に、本章で説明するVACUUM運用のチューニングの流れを示します。

VACUUM運用のチューニングの流れ

3.1 autovacuumのチューニング

3.1.1 autovacuumのチューニング

XID回収を速くするために、多重度を上げる、またはスリープ実行頻度を下げるチューニングを検討してください。以下のパラメーターを調整し、実際に未凍結XIDが増加傾向でなくなることを確認してください。

項番 チューニング項目 パラメーター
1 多重度 [postgresql.conf]
  • autovacuum_max_workers
2 スリープ実行頻度(※) [postgresql.conf]
  • autovacuum_vacuum_cost_limit
  • autovacuum_vacuum_cost_delay
  • VACUUMではI/O回数に応じてコストを計上し、規定値に到達した場合にスリープします。このときのコスト規定値とスリープ時間をpostgresql.confで設定することにより頻度をチューニングします。

3.1.2 autovacuumのチューニングによる性能への影響

周回問題防止autovacuumは、PostgreSQLが自動でVACUUMをスケジュールするため、業務負荷の高い時間に実行される可能性があります。そのため、autovacuumのチューニングによりVACUUMの頻度が上がった場合には、業務影響を与える可能性があります。
以下に、autovacuumが与える影響についてまとめます。

項番 項目 影響内容
1 WAL出力量 autovacuumが出力するWALにより、業務アプリケーションのWAL書き込みが遅延する可能性があります。
また、冗長化構成を構築している場合、WAL量が増大するタイミングで何等かの異常が発生した場合に、業務再開までの時間や実行中の業務アプリケーション性能に影響する可能性があります。詳細は【補足】を参照してください。
2 アプリケーションの異常終了 周回問題防止autovacuumが実行中のテーブルに対して、業務アプリケーションによりSQLコマンド(TRUNCATE、COPYなど)を実行した場合には、排他競合が発生し、業務アプリケーションにタイムアウトなどのエラーが発生する可能性があります。
3 アプリケーションの平均処理時間 以下の要因により業務アプリケーションの性能が遅延する可能性があります。
  • autovacuumがサーバー資源(CPU / DISKなど)を使用
  • WAL出力量が増加
  • 周回問題防止を優先しデッドタプルの回収が遅延

【補足】

プライマリサーバーに異常が発生し切り替え事象が発生した場合、スタンバイサーバーでは、WALを全て適用した後に業務再開します。このため、WAL量が多い場合には、業務再開まで時間がかかる可能性があります。
また、スタンバイサーバー、またはWAL転送ネットワークに異常が発生し切離し事象が発生した場合、プライマリサーバーでは、スタンバイサーバーを切離すことで、実行中の業務アプリケーションのスタンバイサーバーに対してのWAL転送同期待ちを解消します。WAL量が多い場合には、DB領域の不揮発化(CHECKPOINT)を優先するため、切離しに時間がかかり、実行中の業務アプリケーションにタイムアウトが発生する可能性があります。

3.1.3 autovacuumのチューニングによる影響の監視

autovacuumの動作を変更した場合には、「2.1 VACUUM運用の監視項目」の監視に加え、以下を監視してください。監視項目と監視方法を記述します。

項番 監視項目 監視方法
1 WAL出力量 以下のSQLを1分間隔で実行し、WAL出力量を確認してください。
SELECT * FROM pg_stat_wal;
2 アプリケーションの平均処理時間 利用者が採取している業務アプリケーションのレスポンス / スループットの結果を確認してください。
また、原因を特定するために、以下の情報を1分間隔で採取してください。
  • CPU / DISKの性能情報
  • OS情報(sar / iostatなど)
  • WAL出力量
  • 「項番1」を参照してください。
  • デッドタプル量
  • 以下のSQLを実行してください。
    SELECT relname, n_dead_tup, n_live_tup FROM pg_stat_all_tables ORDER BY n_dead_tup DESC LIMIT 100;
  • 排他情報
  • 以下のSQLを実行してください。
    SELECT * FROM pg_locks;

3.1.4 監視結果の評価と対策

autovacuumチューニング時の監視項目に対する評価方法と対策について、以下に記述します。

項番 監視項目 評価と対策
1 WAL出力量 評価
WAL出力量が増加し、アプリケーションのレスポンス / スループットが低下している場合に、以下の対策を実施してください。

対策
3.1.1 autovacuumのチューニング」の多重度 / スリープ実行頻度を調整してください。
2 アプリケーションの平均処理時間 評価
アプリケーションのレスポンス / スループットが低下している場合に、以下の対策を実施してください。

対策
  • CPU / DISKの性能が原因の場合
  • 3.1.1 autovacuumのチューニング」の多重度 / スリープ実行頻度を調整してください。
    上記によるXID回収速度が下がることが看過できない場合は、ハードウェアの増設、またはXID回収の手段としてアプリケーション負荷の少ない時間帯に手動VACUUMを計画することで周回問題防止autovacuumの動作を抑制してください。詳細は「3.2 手動VACUUMの設計とチューニング」を参照してください。

  • デッドタプル数が原因の場合
  • 3.1.1 autovacuumのチューニング」の多重度 / スリープ実行頻度を調整してください。
    上記によるXID回収速度が下がることが看過できない場合は、手動VACUUM運用を計画することで、周回問題防止autovacuumの動作を抑制してください。詳細は「3.2 手動VACUUMの設計とチューニング」を参照してください。

  • 排他競合が原因の場合
  • 業務アプリケーションの実行スケジュールを変更してください。
    不定期に実行される周回問題防止autovacuumに対して対処を講じることが困難である場合は、手動VACUUM運用を計画することで、排他競合する業務と実行時間を調整してください。詳細は「3.2 手動VACUUMの設計とチューニング」を参照してください。

3.2 手動VACUUMの設計とチューニング

3.2.1 手動VACUUM運用の設計

手動VACUUM運用は、「vacuumdbコマンド」または「SQLコマンドのVACUUM」を使用します。ここでは、指定するオプション / パラメーターについての設計方法を記述します。
また、手動VACUUMは、一日一回実施する運用を想定とします。

項番 オプション / パラメーター 設計方法
1 対象テーブル数と対象テーブル名
  • 対象テーブル数は、以下から算出します。
  • 対象テーブル数 = 全テーブル数(※1) ÷ (20億 ÷ XID数(※2)) × 安全係数(※3)
    • ※1
      全テーブル数:以下のSQLから確認できます。
    • SELECT count(*) FROM pg_class WHERE relkind IN ('r', 'm');
    • ※2
      XID数:1日当たりのXIDの発行数を指定します。
    • ※3
      安全係数:1.5を指定します。
  • 対象テーブルは、未凍結XID数が大きいテーブル(※4)から順番に指定します。
    • ※4
      未凍結XID数の大きいテーブル:以下のSQLから確認できます。
    • SELECT c.oid::regclass as table_name, greatest(age(c.relfrozenxid), age(t.relfrozenxid)) as age FROM pg_class c LEFT JOIN pg_class t ON c.reltoastrelid = t.oid WHERE c.relkind IN('r', 'm') ORDER BY age DESC;
2 多重度 多重度を決定するためには、以下の情報を明確にします。
  • サーバーの搭載CPUコア数
  • 手動VACUUMで割り当て可能なCPUコア数

実機検証により、手動VACUUMの運用として許容可能な時間を考慮して多重度を決定してください。

  • 【注意】
    多重度をアップすることで、手動VACUUM時間を短縮することが可能ですが、多重度と手動VACUUMの実行時間の関係は比例ではありません。
    手動VACUUM運用中は、多重度数だけのCPUコアが占有されます。このため、手動VACUUM運用中は、他の業務への性能影響を確認してください。

【注意】

手動VACUUM運用を実施する場合には、周回問題防止autovacuumを抑止します。
この結果、周回問題防止autovacuumによる業務影響がなくなり、業務の安定化を図ることが可能となります。
周回問題防止autovacuumの抑止の方法は、postgresql.confのautovacuum_freeze_max_ageパラメーターに "2000000000"(20億)を設定することを推奨します。

3.2.2 手動VACUUM運用による性能への影響

手動VACUUM運用の実現により、周回問題防止autovacuumによる業務影響がなくなり、業務の安定化を図ることができます。一方で、手動VACUUM実行中には、ハードウェアリソース(CPU / DISK)の使用、および、手動VACUUM実行直後には、WALが大量に出力される可能性があるため、並行して動作する業務に対して性能への影響が懸念されます。
以下に、手動VACUUM運用による影響についてまとめます。

項番 項目 影響内容
1 WAL出力量 手動VACUUMが出力するWALにより、業務アプリケーションのWAL書き込みが遅延する可能性があります。
また、冗長化構成を構築している場合、WAL量が増大するタミングで何らかの異常が発生した場合に、業務再開までの時間や実行中の業務アプリケーション性能に影響する可能性があります。詳細は「3.1.2 autovacuumのチューニングによる性能への影響」の【補足】を参照してください。
2 アプリケーションの異常終了 手動VACUUMが実行中のテーブルに対して、業務アプリケーションで、SQLコマンド(TRUNCATE、COPYなど)を実行した場合には、排他競合が発生し、業務アプリケーションにタイムアウトなどのエラーが発生する可能性があります。

3.2.3 手動VACUUM運用の監視

手動VACUUM運用時には、以下を監視してください。監視項目と監視方法を記述します。

項番 監視項目 監視方法
1 WAL出力量 以下のSQLを1分間隔で実行し、WAL出力量を確認してください。
SELECT * FROM pg_stat_wal;
2 DB領域の不揮発化バッファ数 postgresql.confのlog_checkpointsパラメーターを設定し、サーバーログに出力された不揮発化バッファ数(書き出されたバッファ数)を確認してください。
log_checkpoints = on
3 アプリケーションの平均処理時間 利用者が採取している業務アプリケーションのレスポンス / スループットの結果を確認してください。
また、原因を特定するために、以下の情報を1分間隔で採取してください。
  • CPU / DISKの性能情報
  • OS情報(sar / iostatなど)
  • WAL出力量
  • 「項番1」を参照してください。
  • 排他情報
  • 以下のSQLを実行してください。
    SELECT * FROM pg_locks;
4 手動VACUUM実行時間 手動VACUUM実行時間を確認してください。
また、-v / VERBOSEを指定して手動VACUUMを実行して、テーブルに対してのvacuum統計情報を採取してください。

3.2.4 監視結果の評価と対策

手動VACUUM運用時の監視項目に対する評価方法と対策について、以下に記述します。

項番 監視項目 評価と対策
1 WAL出力量 評価
WAL出力量とDB領域の不揮発化バッファ数が、同時に、著しく増加し、その時にアプリケーションのタイムアウトが発生していた場合に、以下の対策を実施してください。

対策
postgresql.confに以下のパラメーターを設定してください。設定値は、実機検証により、WAL出力量とDB領域の不揮発化バッファ数の減少を確認して決定してください。
  • vacuum_cost_limit
  • vacuum_cost_delay
2 DB領域の不揮発化バッファ数
3 アプリケーションの平均処理時間 評価
アプリケーションのレスポンス / スループットが低下しているしている場合に、以下の対策を実施してください。

対策
  • CPU / DISKの性能が原因の場合
  • ハードウェアの増設をご検討ください。
  • WAL出力量が原因の場合
  • 「項番1」を参照してください。
  • 排他競合が原因の場合
  • 手動VACUUM、または業務アプリケーションの実行スケジュールを変更してください。
4 手動VACUUM実行時間 評価
手動VACUUM実行時間が継続して増加している場合に、以下の対策を実施してください。

対策
  • CPU / DISKの性能が原因の場合
  • ハードウェアの増設をご検討ください。
  • 対象テーブル数が原因の場合
  • 手動VACUUMの設計を見直してください。

ここまで、PostgreSQLにおけるXID周回問題と、その対策として重要なVACUUMのチューニングについて、チューニング後に生じる可能性のある性能影響への対策も含めて解説しました。お使いのシステムに最適なVACUUMの運用を実現するために、ご参考となれば幸いです。

2024年12月27日公開

富士通のソフトウェア公式チャンネル(YouTube)

本コンテンツに関するお問い合わせ

お電話でのお問い合わせ

Webでのお問い合わせ

当社はセキュリティ保護の観点からSSL技術を使用しております。

ページの先頭へ