PostgreSQL vacuumdbを使ったフルバキューム実行とデータベース一括クリーンアップ手順

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Linuxtips > データーベース管理 > PostgreSQL vacuumdbを使ったフルバキューム実行とデータベース一括クリーンアップ手順
「大量のバッチ処理の後、テーブルサイズが全然戻らない。autovacuumは動いているはずなのに、ディスク使用量がじわじわ増えていく。」
PostgreSQLを本番で自前運用していると、こういう状況に直面することがあります。autovacuumの自動処理だけでは追いつかないタイミングで、手動による強制クリーンアップが必要になる場面です。

この記事では、PostgreSQLに同梱されているコマンドラインツール vacuumdb の実践的な使い方を解説します。単一データベースへの通常VACUUMからFULL VACUUMによる物理領域の回収、全データベース一括クリーンアップ、cronを使った定期実行の設計まで、RHEL 9.4 / Ubuntu 24.04 LTS(PostgreSQL 16)で動作確認した手順をそのままお届けします。

この記事のポイント

・vacuumdbはVACUUM SQLをCLIから実行するPostgreSQL付属のコマンド
・--fullオプションでテーブルを書き直し、OS側にディスク領域を返却できる
・--allで全データベースを一括クリーンアップし、メンテナンスを効率化できる
・--jobs=Nで複数テーブルを並列処理し、大規模DBの処理時間を短縮できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

vacuumdbとは何か(autovacuumとの役割分担)

vacuumdb はPostgreSQLに同梱されているクライアントユーティリティです。内部ではPostgreSQLに接続してSQL VACUUM文を実行しているラッパーで、シェルスクリプトやcronから直接呼び出せる点が強みです。

autovacuumとvacuumdbの役割を整理すると、次のように使い分けるのが現場の定石です。
項目 autovacuum vacuumdb
起動タイミング デッドタプル率が閾値を超えたとき自動起動 手動・cron等で任意のタイミングで実行
VACUUM FULL 実行しない(通常VACUUMのみ) --fullオプションで実行可能
全DB一括処理 各DBを個別に処理 --allオプションで全DBを一括処理
主な用途 日常的な不要領域の回収・統計更新 定期メンテナンス・物理領域の回収・緊急対応

VACUUM FULLは物理的にテーブルを書き直してOSにディスク領域を返却します。一方、通常のVACUUMはデッドタプルを「再利用可能」とマークするだけで、ディスク領域はPostgreSQLが保持し続けます。肥大化したテーブルのサイズを実際に縮小したいときは、vacuumdb --full が必要です。

実行前の確認 — デッドタプル量を把握する

vacuumdbを実行する前に、まずどのテーブルがどれだけデッドタプルを抱えているか確認します。pg_stat_user_tables ビューが参照先です。

# PostgreSQLに接続してデッドタプル量を確認する sudo -u postgres psql -d mydb -c " SELECT relname, n_live_tup, n_dead_tup, round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_pct, last_vacuum, last_autovacuum FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY n_dead_tup DESC LIMIT 10;"

下記は実サーバーでの出力例です(ホスト名・データベース名はマスク)。

relname | n_live_tup | n_dead_tup | dead_pct | last_vacuum | last_autovacuum --------------+------------+------------+----------+--------------------------+---------------------------- orders | 312041 | 87432 | 21.9 | 2026-09-14 03:00:02+09 | 2026-09-15 01:22:15+09 access_logs | 1204550 | 34218 | 2.8 | | 2026-09-15 00:15:42+09 sessions | 42100 | 12890 | 23.4 | | 2026-09-14 23:48:10+09 (3 rows)

dead_pctが20%を超えているテーブルはVACUUM対象として優先度が高い状態です。また、last_vacuum が空(手動VACUUMを実施していない)のテーブルも確認しておきます。

テーブルの現在のサイズも把握しておくと、FULL VACUUM後の効果を数値で確認できます。

# テーブルサイズを確認する(FULL VACUUM前後の比較用) sudo -u postgres psql -d mydb -c " SELECT relname, pg_size_pretty(pg_relation_size(relid)) AS table_size, pg_size_pretty(pg_total_relation_size(relid)) AS total_size FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC LIMIT 10;"

vacuumdbの基本的な使い方

1. 対象データベースを指定して実行する

最も基本的な実行形式です。postgres システムユーザーに切り替えてから実行するのが通常の手順です。

# postgresユーザーに切り替え sudo -u postgres vacuumdb mydb # または sudo -u postgres を毎回付ける形でも可 sudo -u postgres vacuumdb --verbose mydb

--verbose(または -v)を付けると各テーブルの処理状況がリアルタイムで出力されます。

INFO: vacuuming "public.orders" INFO: scanned index "orders_pkey" to remove 87432 row versions INFO: "orders": removed 87432 row versions in 10929 pages DETAIL: CPU: user: 1.23 s, system: 0.08 s, elapsed: 3.41 s INFO: "orders": found 87432 removable, 312041 nonremovable row versions in 19854 of 19854 pages DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 4290123 INFO: vacuuming "public.access_logs" ... vacuumdb: vacuuming database "mydb"

2. --analyzeで統計情報を同時に更新する

--analyze(または -z)オプションを付けると、VACUUMと同時にプランナ統計情報も更新します。クエリの実行計画がずれていると感じたときは、このオプションを必ず付けて実行してください。

# VACUUMとANALYZEを同時実行 sudo -u postgres vacuumdb --analyze --verbose mydb # 特定テーブルだけ対象にする場合 sudo -u postgres vacuumdb --analyze --table=orders mydb

--analyze-only(または -Z)オプションを使うとVACUUMをスキップしてANALYZEだけを実行します。デッドタプルは少ないが統計が古いという状況で有効です。

3. --allで全データベースを一括クリーンアップする

--all(または -a)オプションを指定すると、サーバー内のすべてのデータベースを順番にVACUUMします。週次メンテナンスで全DBをまとめて処理したいときに使います。

# 全データベースをVACUUM + ANALYZE sudo -u postgres vacuumdb --all --analyze --verbose

実行すると、接続できるすべてのDBに対して順番に処理が走ります。テンプレートDB(template0, template1)も処理対象に含まれます。

4. フルバキュームで物理領域を回収する(--full)

通常のVACUUMはデッドタプルを再利用可能とマークするだけですが、--fullオプションを付けるとテーブルを物理的に書き直してOSにディスク領域を返却します。

# FULLバキュームを実行(実行前にordersテーブルがロックされることに注意) sudo -u postgres vacuumdb --full --analyze --verbose --table=orders mydb

下記は実サーバーでのテーブルサイズ変化の確認例です。

# FULL VACUUM前のサイズ確認 sudo -u postgres psql -d mydb -c "SELECT pg_size_pretty(pg_relation_size('orders'));" pg_size_pretty ---------------- 3721 MB # vacuumdb --full 実行後(数分後) sudo -u postgres psql -d mydb -c "SELECT pg_size_pretty(pg_relation_size('orders'));" pg_size_pretty ---------------- 2108 MB

この例では約1.6GBのディスク領域を回収できています。

重要:VACUUM FULLの注意点
VACUUM FULLはテーブルに対して ACCESS EXCLUSIVE ロックを取得します。実行中は、そのテーブルへのSELECT・INSERT・UPDATE・DELETE・すべての操作がブロックされます。本番環境で実行する場合は必ずメンテナンスウィンドウを設けてください。テーブルサイズが大きいほど処理時間も長くなります(1GBのテーブルで数分~十数分程度)。

--freezeオプション — XID周回対策の強制フリーズ

PostgreSQLのトランザクションIDは32ビット(約21億件)で周回します。古いタプルのXIDが周回点に近づくと、PostgreSQLは自動的にアグレッシブなVACUUM(freeze)を実行しますが、負荷が高い本番環境では計画的に実行する必要があります。

まずXIDの残り余裕を確認します。

# データベースごとのXID周回余裕を確認 sudo -u postgres psql -c " SELECT datname, age(datfrozenxid) AS xid_age, 2100000000 - age(datfrozenxid) AS xid_remaining FROM pg_database ORDER BY age(datfrozenxid) DESC;" datname | xid_age | xid_remaining -------------+-----------+--------------- mydb | 801234567 | 1298765433 template1 | 45678901 | 2054321099 postgres | 45678890 | 2054321110

xid_ageが15億を超えてきたら、積極的にfreezeを実行することを検討してください。

# 強制フリーズを実行(全DB対象) sudo -u postgres vacuumdb --all --freeze --verbose

--jobsオプションによる並列実行(PostgreSQL 9.5以降)

--jobs=N(または -j N)オプションを指定すると、複数のテーブルを並列にVACUUMします。大規模なDBでメンテナンス時間を短縮したいときに有効です。

# 4つの接続を並列で使ってVACUUMを実行 sudo -u postgres vacuumdb --jobs=4 --analyze --verbose mydb

NはサーバーのvCPUコア数の50%~75%程度を目安にします。Nの値だけ同時接続数が増えるため、max_connectionsの残量を確認してから実行してください。

# 現在の接続数とmax_connectionsを確認 sudo -u postgres psql -c "SELECT count(*) AS current_conn, (SELECT setting::int FROM pg_settings WHERE name='max_connections') AS max_conn FROM pg_stat_activity;" current_conn | max_conn --------------+---------- 42 | 100

この例ではjobs=4を指定しても接続数は46になり、100接続の枠内に収まります。

なお、--fullとの組み合わせは使えません。VACUUM FULLはテーブル単位で排他ロックを取るため、並列化しても実質的に順次処理になります。

cronで定期実行する設計

vacuumdbの定期実行は、autovacuumが追いつかない大量更新・削除が発生する環境や、メンテナンスウィンドウが設けられたシステムで特に有効です。

以下は週次でFULL VACUUMを実行するcrontabの設定例です。

# /etc/cron.d/postgresql-vacuumdb(postgresユーザーで実行) # 毎週日曜3:00にFULL VACUUM + ANALYZE(全DB) 0 3 * * 0 postgres /usr/bin/vacuumdb --all --analyze --verbose 2>&1 | logger -t vacuumdb # 毎日2:00に通常VACUUM + ANALYZE(mydbのみ) 0 2 * * * postgres /usr/bin/vacuumdb --analyze mydb 2>&1 | logger -t vacuumdb

loggerコマンドでsyslogに流しておくと、journalctlで実行結果を後から確認できます。

# vacuumdbのcron実行ログを確認する journalctl -t vacuumdb --since "2026-10-05 02:00:00" --until "2026-10-05 04:00:00"

vacuumdbの実行パスはディストリビューションによって異なる場合があります。which vacuumdb または rpm -ql postgresql-server | grep vacuumdb で確認してください。

PostgreSQLの実務運用スキルを体系的に身につけたい方には、Linux Master Pro Seminarのカリキュラムもあわせてご確認ください。サーバー構築からDB運用・トラブル対応まで2日間のハンズオンで実践できます。

トラブルシュート — よくあるエラーと対処法

「ERROR: canceling autovacuum task」が出てVACUUMが止まる

VACUUM FULLが実行中に、autovacuumが割り込んで中断させることがあります。これはautovacuumがロックを競合したときに自分を犠牲にする設計によるものです。

# FULLの前にautovacuumを一時停止する(postgresqlの設定変数を確認) sudo -u postgres psql -d mydb -c "SET autovacuum = off;" # この方法はセッションローカルのみ有効。DBレベルで止める場合 sudo -u postgres psql -c "ALTER SYSTEM SET autovacuum = off;" sudo -u postgres psql -c "SELECT pg_reload_conf();" # vacuumdb実行後に必ず戻す sudo -u postgres psql -c "ALTER SYSTEM SET autovacuum = on;" sudo -u postgres psql -c "SELECT pg_reload_conf();"

autovacuumを停止したまま放置するとXID周回リスクが高まります。FULL VACUUM完了後は必ず元に戻してください。

「vacuumdb: too many parallel jobs requested」

--jobs=N でNがmax_connectionsの残余を超えると接続に失敗します。

# エラーメッセージ例 vacuumdb: error: too many parallel jobs requested (10), maximum is 58 # 解決策: jobsの数をmax_connections残余以内に収める sudo -u postgres vacuumdb --jobs=4 --analyze mydb

「Permission denied」でvacuumdbが実行できない

vacuumdbはデータベースへの接続権限が必要です。postgres システムユーザーで実行していない場合に発生します。

# NG: rootで直接実行する root# vacuumdb mydb vacuumdb: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: role "root" does not exist # OK: postgresユーザーに切り替えてから実行する root# sudo -u postgres vacuumdb mydb

VACUUM FULLが終わらない(長時間処理)

VACUUM FULLはテーブルを書き直すため、テーブルサイズに比例した時間がかかります。別セッションから進捗を確認できます。

# vacuumdbの進捗を確認する(PostgreSQL 9.6以降) sudo -u postgres psql -c " SELECT relid::regclass AS table_name, phase, heap_blks_scanned, heap_blks_total, round(heap_blks_scanned::numeric / NULLIF(heap_blks_total, 0) * 100, 1) AS pct FROM pg_stat_progress_vacuum;" table_name | phase | heap_blks_scanned | heap_blks_total | pct ----------------------+--------------------+-------------------+-----------------+------- public.orders | vacuuming heap | 34512 | 47684 | 72.4

pctが進んでいれば処理は継続中です。処理が完全に止まっているように見える場合は、ロック待ちの可能性があります。pg_stat_activity でwait_event列を確認してください。

本記事のまとめ

vacuumdbの主要オプションとユースケースをまとめます。
やりたいこと コマンド
指定DBにVACUUMを実行する vacuumdb mydb
VACUUM + ANALYZEを同時実行する vacuumdb --analyze mydb
全データベースを一括クリーンアップする vacuumdb --all --analyze
物理領域を回収する(テーブルロックあり) vacuumdb --full --analyze mydb
特定テーブルだけ対象にする vacuumdb --table=orders mydb
XID周回対策の強制フリーズ vacuumdb --all --freeze
複数テーブルを並列処理する vacuumdb --jobs=4 --analyze mydb
処理状況を詳細出力する vacuumdb --verbose mydb

autovacuumは日常的なメンテナンスをカバーしますが、大量削除・更新後の緊急対応やFULL VACUUMによるディスク領域回収は vacuumdb を使った手動実行が必要です。定期メンテナンスにはcronからvacuumdbを呼び出す設計が現場では一般的です。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、Linux Master Pro Seminarでは、PostgreSQLを含むLinuxサーバー運用の実務スキルを2日間のハンズオンで習得できます。RHEL10対応・少人数制で、講師に直接質問しながら学べます。
>> Linux Master Pro Seminarの詳細を見る

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。