Postgres AUTOVACUUM を解き明かす:トランザクションIDラッパーラウンド、ブロート、パフォーマンスのために
Wenhao Wang
Dev Intern · Leapcell

はじめに
PostgreSQL は、その堅牢性と ACID 準拠で知られていますが、内部メカニズムを理解することは、高性能で安定したデータベースを維持するための鍵となります。その中でも、AUTOVACUUM プロセスは、バックグラウンドで静かに動作することが多いですが、その重要性を過小評価することはできません。多くのデータベース管理者、特に PostgreSQL の初心者の方は、パフォーマンスのボトルネックが発生したり、最悪の場合、トランザクションIDラッパーラウンドによる壊滅的なデータベースシャットダウンが発生するまで、その重要な役割を見過ごしているかもしれません。この記事では、AUTOVACUUM のベールを剥がし、データ破損の防止、テーブルブロートとの戦い、そして最終的には最適なデータベースパフォーマンスの確保におけるその基本的な目的を探求します。そのコア機能 dissecting することで、PostgreSQL システムのこの不可欠なコンポーネントを効果的に監視およびチューニングするための知識を身につけることができます。
AUTOVACUUM の内部動作
AUTOVACUUM 自体に飛び込む前に、それがやり取りするいくつかのコア PostgreSQL の概念を理解することが重要です。
主要な概念
- MVCC (Multi-Version Concurrency Control): PostgreSQL は MVCC を実装しており、これにより複数のトランザクションがお互いをロックすることなく、同時に同じデータにアクセスできます。行が更新または削除されるとき、PostgreSQL は古いバージョンをすぐに削除しません。代わりに、「デッド」としてマークし、新しいバージョンを作成します。これらのデッドタプルはストレージスペースを消費し、回収する必要があります。
- Transaction ID (XID): PostgreSQL のすべてのトランザクションには、一意の 32 ビットトランザクションID (XID) が割り当てられます。これらの XID は、MVCC ルールに基づいて、どの行バージョンが異なるトランザクションから見えるかを決定するために使用されます。
- Transaction ID Wraparound (トランザクションIDラッパーラウンド): XID は 32 ビット整数であるため、最終的にはゼロに「ラップアラウンド」します。システムが定期的に古いトランザクションを「フリーズ」しない場合(XID を永続的にコミット済みとしてマークする)、データベースは最終的に古いコミット済みトランザクションを将来の未コミットトランザクションとして扱い始めます。これは、PostgreSQL が impending wraparound を検出すると、開始を拒否するハードリミットを持っているため、データ破損やデータベースシャットダウンにつながる可能性があります。
- Table Bloat (テーブルブロート): MVCC で述べたように、デッドタプルが時間とともに蓄積します。データページ内のこの未使用スペースは、テーブルブロートと呼ばれます。ブロートは、データベースが不要なほど多くのデータをディスクから読み取る必要があるため、クエリパフォーマンスを大幅に低下させ、I/O を増加させ、キャッシュの効率を低下させます。
AUTOVACUUM の目的
AUTOVACUUM は、データベース内のテーブルに対して VACUUM および ANALYZE コマンドを実行する自動プロセスです。その主な目的は次のとおりです。
- トランザクションIDラッパーラウンドの防止: これは、おそらく
AUTOVACUUMの最も重要な機能です。テーブルをスキャンし、古いタプルを「フリーズ」して、XID 可視性をリセットし、それらが常にコミット済みとして考慮されるようにします。 - デッドタプルからのストレージの回収:
AUTOVACUUMは、デッドタプルによって占有されているスペースを特定および再利用可能なものとしてマークし、テーブルブロートを軽減し、テーブルサイズを管理可能に保ちます。スペースを回収し、同じテーブル内の新しいデータ挿入で利用できるようにします。VACUUM FULL(AUTOVACUUMは実行しません)またはALTER TABLE ... SET (autovacuum_vacuum_truncate = true)(最後のページの場合)が実行されない限り、ディスク上のファイルサイズを実際に縮小するわけではありません。 - クエリプランナーの統計情報の更新:
AUTOVACUUMのANALYZE部分は、テーブルとインデックスのデータ分布に関する統計情報を収集します。この情報は、クエリプランナーが効率的な実行計画を選択するために不可欠であり、クエリパフォーマンスに直接影響します。
AUTOVACUUM の仕組み
AUTOVACUUM は、一連の独立したバックグラウンドプロセスとして実行されます。専用のランチャープロセスが、バキュームまたは分析が必要なテーブルをデータベースで監視します。トリガーされると、実際の VACUUM および ANALYZE 操作を実行するためのワーカープロセスが生成されます。
AUTOVACUUM のトリガーは、さまざまなパラメータを通じて設定可能です。
autovacuum_vacuum_thresholdおよびautovacuum_vacuum_scale_factor: デッドタプルの数がautovacuum_vacuum_threshold + (autovacuum_vacuum_scale_factor * reltuples)を超えた場合、テーブルはVACUUMの対象と見なされます。たとえば、autovacuum_vacuum_thresholdが 50 でautovacuum_vacuum_scale_factorが 0.1 (10%) の場合、1000 行のテーブルは 150 個のデッドタプルが蓄積したときにバキュームされます。autovacuum_analyze_thresholdおよびautovacuum_analyze_scale_factor: バキュームと同様に、挿入、更新、または削除されたタプルの数がautovacuum_analyze_threshold + (autovacuum_analyze_scale_factor * reltuples)を超えた場合にANALYZEがトリガーされます。- トランザクションIDラッパーラウンド防止:
AUTOVACUUMは、vacuum_freeze_min_ageおよびautovacuum_freeze_max_ageに基づいてバキュームをトリガーします。max_age_since_last_vacuum(最後の VACUUM 以降のテーブル内の任意の XID の最大年齢)がautovacuum_freeze_max_age(デフォルト 2000 万トランザクション)に近づくと、AUTOVACUUMはフリーズ操作を優先してラッパーラウンドを防ぎます。
AUTOVACUUM の監視とチューニング
効果的な AUTOVACUUM 管理には、そのアクティビティの監視とパラメータのチューニングの両方が必要です。
監視
pg_stat_all_tables ビューを使用して、AUTOVACUUM のアクティビティとテーブル統計情報を確認できます。
SELECT relname, n_live_tuples, n_dead_tuples, last_autovacuum, last_autoanalyze, autovacuum_count, autoanalyze_count, age(relfrozenxid) AS xid_age_since_last_vacuum -- wraparoundに重要 FROM pg_stat_all_tables WHERE schemaname = 'public' ORDER BY n_dead_tuples DESC;
このクエリは、ライブタプルとデッドタプル、AUTOVACUUM/AUTOANALYZE が最後に実行された時刻、そして最も重要な xid_age_since_last_vacuum を表示します。これは、テーブルが autovacuum_freeze_max_age にどれだけ近づいているかを示します。この値が高すぎる(例:1億8000万を超える)場合、AUTOVACUUM が苦労している可能性が高いことを強く示唆しています。
pg_settings ビューで現在の AUTOVACUUM 設定を確認することもできます。
SELECT name, setting, unit, short_desc FROM pg_settings WHERE name LIKE 'autovacuum%';
チューニングパラメータ
AUTOVACUUM のチューニングは、問題を防ぐのに十分な頻度で実行されるようにすることと、過剰なリソース消費を引き起こさないようにすることとの間のバランス調整です。以下に、postgresql.conf で考慮すべきいくつかの重要なパラメータを示します。
autovacuum = on: (デフォルトon)AUTOVACUUMが有効であることを確認します。本番環境でこれをオフにしないでください。autovacuum_max_workers: (デフォルト 3) 同時に実行できるAUTOVACUUMワーカープロセスの最大数。これを増やすと、重い書き込みワークロードに対応するのに役立ちますが、より多くのリソースを消費します。autovacuum_vacuum_cost_delay: (デフォルト 2ms)AUTOVACUUMがautovacuum_vacuum_cost_limitコストを蓄積した後、スリープする時間(ミリ秒)。これを短くすると、AUTOVACUUMはより積極的に動作しますが、より多くの I/O を消費します。autovacuum_vacuum_cost_limit: (デフォルト -1、vacuum_cost_limitを使用することを意味します)AUTOVACUUMがスリープする前に実行できるバキューム作業量(任意の単位)。autovacuum_vacuum_scale_factor: (デフォルト 0.2、または 20%) 重要なパラメータ。常に変化する大きなテーブルの場合、これを減らす(例:0.05 または 5%)と、バキュームがより頻繁にトリガーされ、ブロートが軽減される可能性があります。autovacuum_analyze_scale_factor: (デフォルト 0.1、または 10%) 上記と同様のロジックで、統計情報の更新に影響します。autovacuum_freeze_max_age: (デフォルト 200000000) テーブルの XID 値が、ラッパーラウンドを防ぐための積極的なAUTOVACUUMがトリガーされる前に到達できる最大年齢(トランザクション単位)。これは、ラッパーラウンドを防ぐための重要な安全パラメータです。その意味を完全に理解していない限り、通常はこれを変更しないでください。vacuum_freeze_table_age: (デフォルト 150000000) テーブルがこの年齢に達すると、AUTOVACUUMは明示的にVACUUM FREEZEを実行して XID をフリーズします。これはautovacuum_freeze_max_ageよりもソフトなトリガーです。
チューニング例:
数百万行の更新と削除が頻繁に行われ、重大なブロートと時折のパフォーマンスの低下を引き起こしている、大規模な orders テーブルを想像してください。
-- テーブルの現在の autovacuum 設定を確認します(テーブルレベルのオーバーライドはグローバル設定)。 SELECT relname, reloptions FROM pg_class WHERE relname = 'orders'; -- たとえば、十分に頻繁にバキュームされていないとします。 -- テーブルレベルの autovacuum パラメータをより積極的に設定できます。 ALTER TABLE orders SET (autovacuum_vacuum_scale_factor = 0.01); -- 1% のデッドタプル後にバキューム ALTER TABLE orders SET (autovacuum_vacuum_cost_delay = 5); -- スリープ時間を短縮(より積極的) ALTER TABLE orders SET (autovacuum_analyze_scale_factor = 0.005); -- 0.5% 変更後に分析
この例では、他のテーブルに影響を与えることなく、orders テーブルの AUTOVACUUM をより応答性が高くなります。本番環境に適用する前に、常にステージング環境でチューニング変更をテストしてください。過度の積極性は、リソース使用量と競合の増加につながる可能性があります。
手動でのブロート対処(AUTOVACUUM が十分でない場合)
AUTOVACUUM はスペースを回収しますが、最後のページが切り捨てられる場合を除き、ディスク上の実際のテーブルファイルサイズを縮小しないことがよくあります。深刻なブロート、特にインデックスの場合、VACUUM FULL または REINDEX が必要になる場合があります。
VACUUM FULL table_name;: すべてのスペースを回収し、ファイルサイズを実際に縮小します。これにはテーブルに対する排他ロックが必要であり、実行中は他の操作が不可能になります。非常に慎重に使用してください。REINDEX TABLE table_name;またはREINDEX INDEX index_name;: インデックスを最初から再構築し、ブロートを完全に解消します。VACUUM FULLと同様に、これはインデックス/テーブル(コマンドによって異なります)に対する排他ロックが必要です。pg_repackまたはpg_squeeze(ダウンタイムなしのソリューション):VACUUM FULLまたはREINDEXのダウンタイムが許容できない大規模な本番テーブルの場合、pg_repackやpg_squeeze(オープンソース拡張機能)のようなツールは、プロセス中に排他ロックをほとんどかけずに、これらの操作をオンラインで実行できます。これらは、バックグラウンドで新しいテーブルを作成し、元のテーブルと入れ替えることで機能します。
結論
AUTOVACUUM は PostgreSQL の縁の下の力持ちであり、データ整合性と最適なパフォーマンスを静かに確保しています。トランザクションIDラッパーラウンドの防止、テーブルブロートの軽減、および正確なクエリプランナー統計情報の維持におけるその役割を理解することで、データベースをプロアクティブにチューニングして、回復力と速度を向上させることができます。定期的な監視と慎重なパラメータ調整は、PostgreSQL システムを健全で応答性の高い状態に保つために不可欠です。
AUTOVACUUM は PostgreSQL を自己修復させ、インテリジェントなバックグラウンドメンテナンスを通じて、知らぬ間に破損したりパフォーマンスが低下したりするのを防ぎます。

