TL

オートバキュームとテーブル膨張

テーブルが際限なく肥大化する前に、autovacuumのしきい値調整と長時間トランザクション対策を押さえれば、性能劣化とディスク圧迫を未然に防げます。

応用データベースPostgreSQLVACUUM運用性能チューニング最終更新: 2026-06-21
TL;DR要点だけ先に
  • 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を消費するだけの「空振り」になります。

空振りVACUUMは統計にも現れにくい

autovacuumのログには実行自体は記録されますが、「回収できた行数がほぼゼロ」であることは見落とされがちです。pg_stat_activityxact_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 FULLpg_repackREINDEX CONCURRENTLYで実サイズを縮小する。回収境界の原理は MVCC のガベージコレクション、行ロックとの関係は ロックと MVCC、接続の滞留対策は コネクションプーリング も参照。

データベース Article

オートバキュームとテーブル膨張を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

データベース

比較で見る軸

難易度: advanced / カテゴリ: データベース / タグ数: 5

導入後に効く点

cost-based delayやワーカー数などautovacuumの実行速度パラメータを調整し、追いつかない状態を解消する。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
データベース
タグ数
5

判断チェックリスト

  • 自社の用途が「データベース / PostgreSQL」に近いか確認する。
  • 強みである「dead tupleの蓄積速度としきい値の関係を式で理解し、大規模テーブルほどscale factorを下げて起動を早める。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

データベースPostgreSQLVACUUM運用性能チューニングデータベースPostgreSQLVACUUM
参考: 公式情報