オートバキュームとテーブル膨張
テーブルが際限なく肥大化する前に、autovacuumのしきい値調整と長時間トランザクション対策を押さえれば、性能劣化とディスク圧迫を未然に防げます。
- 1.dead tupleの蓄積速度としきい値の関係を式で理解し、大規模テーブルほどscale factorを下げて起動を早める。
- 2.cost-based delayやワーカー数などautovacuumの実行速度パラメータを調整し、追いつかない状態を解消する。
- 3.pgstattupleやbloat推定クエリで膨張を数値化し、長時間トランザクションを断つ運用フローを確立する。
この記事のスコープ
MVCCがなぜ不要な行を生み出し、回収器がどんな原理で境界を計算するかは MVCC のガベージコレクション で扱いました。本稿はその先、autovacuumを実運用でどう調整し、膨張(bloat)をどう検知・対処するかに絞ります。原理を知っていても、しきい値の初期値のままでは大規模テーブルで簡単に破綻します。
dead tupleが引き起こす二重の膨張
UPDATEやDELETEは既存行を消さず、新版を追加して旧版を「見えなくする」だけです。旧版(dead tuple)は次のVACUUMまで物理的にページを占有し続けます。これがテーブル膨張の直接原因ですが、実はインデックス側の膨張はより深刻になりがちです。
理由は、B-Treeインデックスのエントリは対応するヒープ行が死んでもすぐには詰め直されず、空になったリーフページも他の値の挿入に再利用されるまで放置されるためです。更新頻度の高いテーブルほど、ヒープよりインデックスの肥大化率が高くなる傾向があります。
膨張とは、実際に生きたデータが必要とする理論サイズと、ディスク上のファイルサイズとの乖離です。乖離が大きいほどキャッシュヒット率が下がり、同じ行を読むためのI/Oが増えて性能が劣化します。バキュームは領域を空きリストに戻すだけでファイルを縮めないため、一度膨張したテーブルは能動的な対処なしには縮みません。
autovacuumのトリガ条件を式で捉える
autovacuumがテーブルを対象にするかどうかは、推定dead tuple数としきい値の比較で決まります。
vacuum_threshold = autovacuum_vacuum_threshold
+ autovacuum_vacuum_scale_factor * 推定行数
analyze_threshold = autovacuum_analyze_threshold
+ autovacuum_analyze_scale_factor * 推定行数
dead_tuple数 > vacuum_threshold なら VACUUM 対象
変更行数 > analyze_threshold なら ANALYZE 対象
既定値はscale factorが0.2(vacuum)/ 0.1(analyze)、thresholdが50です。ここで見落としやすいのが、scale factorは行数に比例するため、行数が多いテーブルほど絶対量として大量のdead tupleを溜め込んでからでないと起動しないという性質です。1000万行のテーブルなら、既定のscale factor 0.2で約200万行のdead tupleが溜まるまで待つことになり、その間ずっとインデックスとヒープが肥大化します。
ALTER TABLE ... SET (autovacuum_vacuum_scale_factor = 0.02, autovacuum_vacuum_threshold = 5000) のようにテーブル単位で上書きし、大規模・高頻度更新のテーブルだけ起動を早めるのが定石です。全テーブル一律で下げるとautovacuumワーカーの奪い合いが起き、かえって追いつかなくなります。
実行速度を絞る仕組みとその副作用
VACUUMはI/Oを消費するため、既定ではコストベースの遅延(cost-based vacuum delay)で速度が抑えられています。
1回のVACUUM処理中:
ページ読み書きごとにコストを加算
累積コスト >= autovacuum_vacuum_cost_limit なら
autovacuum_vacuum_cost_delay だけ休止
この仕組み自体は他の処理への影響を抑えるために必要ですが、更新量が多い環境ではバキュームの処理速度がdead tupleの発生速度に追いつかない状態に陥ります。兆候は、あるテーブルに対してautovacuumが常に稼働中なのに死タプル数がむしろ増え続けることです。対処はautovacuum_vacuum_cost_limitを引き上げる、autovacuum_max_workersを増やす、あるいはストレージのI/O余力を踏まえてcost_delayを短くすることです。ただしワーカー数を増やしすぎると同時実行されるVACUUM同士がI/Oを奪い合い、逆効果になる点には注意が必要です。
長時間トランザクションによる停滞
しきい値やコスト設定をいくら最適化しても、回収可能ラインを固定する長時間トランザクションが1本あれば効果は無効化されます。VACUUMは実行はされても、xmin horizonより新しいdead tupleを一切回収できず、CPUとI/Oを消費するだけの「空振り」になります。
autovacuumのログには実行自体は記録されますが、「回収できた行数がほぼゼロ」であることは見落とされがちです。pg_stat_activityのxact_startが古い接続と、対象テーブルのn_dead_tupが減らない事実を突き合わせて初めて気づけます。バッチ処理やレプリケーションスロット、放置されたPREPARE TRANSACTIONも同様に境界を固定するため点検対象に含めます。
膨張の監視と対処
膨張の定量化にはpgstattuple拡張が有効です。pgstattuple('table')は実際の空き率(dead tuple率)を直接計算し、統計情報からの推定より正確です。ただし対象テーブル全体をスキャンするためコストが高く、本番では夜間バッチや軽量な近似クエリ(pg_statsのヒストグラムとpg_class.relpagesの比較)を併用します。
| 対処 | 効果 | コスト・制約 |
|---|---|---|
| autovacuum設定の見直し | 膨張の発生自体を抑制 | テーブルごとの調整が必要 |
| VACUUM(手動) | 空きリストへの回収を即時実行 | ロックは軽いが完了まで時間 |
| VACUUM FULL | ファイルサイズを実際に縮小 | テーブル全体を排他ロックし書き直す |
| pg_repack等の外部ツール | オンラインでファイル縮小 | 追加ディスクと拡張導入が必要 |
VACUUM FULLは膨張を根本的に解消しますが、処理中はテーブルへの読み書きがブロックされるため、多くの現場ではpg_repackのように新テーブルへ行をコピーしてから短時間の切り替えで置き換える手法が使われます。インデックスの膨張だけが問題ならREINDEX CONCURRENTLYでロックを避けつつ再構築する選択肢もあります。
まとめ
- dead tupleの蓄積はヒープよりインデックスで深刻化しやすく、膨張は「理論サイズと実ファイルサイズの乖離」として現れる。
- autovacuumのしきい値はscale factorに比例するため、大規模テーブルほど個別のscale factor引き下げが必須。
- cost-based delayによる速度制限が更新速度に追いつかない場合はcost_limitやワーカー数を調整するが、増やしすぎはI/O競合を招く。
- 長時間トランザクションはVACUUMを「空振り」させる根本原因であり、
pg_stat_activityとdead tuple数の突き合わせで検知する。 - 膨張が既に進んだテーブルは通常のVACUUMでは縮まず、
VACUUM FULLやpg_repack、REINDEX CONCURRENTLYで実サイズを縮小する。回収境界の原理は MVCC のガベージコレクション、行ロックとの関係は ロックと MVCC、接続の滞留対策は コネクションプーリング も参照。
データベース Article
オートバキュームとテーブル膨張を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
データベース
比較で見る軸
難易度: advanced / カテゴリ: データベース / タグ数: 5
導入後に効く点
cost-based delayやワーカー数などautovacuumの実行速度パラメータを調整し、追いつかない状態を解消する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- データベース
- タグ数
- 5
判断チェックリスト
- 自社の用途が「データベース / PostgreSQL」に近いか確認する。
- 強みである「dead tupleの蓄積速度としきい値の関係を式で理解し、大規模テーブルほどscale factorを下げて起動を早める。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。