PostgreSQL 'No space left on device' エラーの修正: could not write to file base/...

intermediate🐘 PostgreSQL2026-03-18| PostgreSQL 12〜16、Linux(Ubuntu/Debian/CentOS/RHEL)、データディレクトリがフルのディスクまたはパーティション上にある任意の環境

Error Message

ERROR: could not write to file "base/..." No space left on device
#postgresql#ディスク容量#ストレージ

TL;DR

PostgreSQL のデータパーティションがフルです。すぐにディスク容量を確保し、クラッシュしている場合は PostgreSQL を再起動してください。

# どのパーティションがフルかを確認
df -h

# PostgreSQL データディレクトリのサイズを確認
du -sh /var/lib/postgresql/*/main

# 手っ取り早い対処 — 古いログと一時ファイルを削除
sudo find /var/log/postgresql -name '*.log' -mtime +7 -delete
sudo find /tmp -name 'pgsql_tmp*' -delete

# PostgreSQL が停止している場合は再起動
sudo systemctl restart postgresql

実際に何が起きているか

PostgreSQL はすべてのもの(行、インデックス、WAL セグメント、一時ソートファイル)をファイルシステムに書き込みます。データディレクトリ(デフォルトでは /var/lib/postgresql)をホストするパーティションが 100% に達すると、書き込みの試みは即座に失敗します:

ERROR: could not write to file "base/pgsql_tmp/pgsqltmp12345.0": No space left on device
# または
ERROR: could not write to file "base/16384/1259": No space left on device
# または
PANIC: could not write to file "pg_wal/000000010000000000000001": No space left on device

base/ 以降のパスは、何が失敗したかを正確に教えてくれます:ソート/ハッシュ結合の一時ファイル、ヒープファイル、または WAL セグメントです。WAL の失敗が最も深刻です。PostgreSQL はデータの整合性を保護するためにパニックになり、完全にシャットダウンします — 迅速に対処してください。

ステップ 1 — 何がスペースを消費しているか診断する

# すべてのパーティションと現在の使用状況を確認
df -h

# postgres データディレクトリ以下の最大ディレクトリを探す
sudo du -sh /var/lib/postgresql/*/main/* | sort -rh | head -20

# WAL の蓄積を特定して確認
sudo du -sh /var/lib/postgresql/*/main/pg_wal

# 一時ファイル(不正なクエリで急速に膨張することがある)
sudo du -sh /var/lib/postgresql/*/main/base/pgsql_tmp 2>/dev/null || echo "一時ディレクトリなし"

# PostgreSQL ログディレクトリを確認
du -sh /var/log/postgresql/

よくある原因(頻度順):

  • WAL の蓄積 — 古いレプリケーションスロットや失敗した archive_command が、数百の 16 MB WAL セグメントを静かに保持している
  • 一時ファイル — ディスク上で大規模なソートまたはハッシュ結合を行う暴走クエリ
  • テーブルの肥大化 — まだバキュームされていないデッドタプル、または幅広テーブルの TOAST オーバーフロー
  • ログファイル — 本番環境で誤って有効のままになっている詳細ログ
  • OS レベルのファイル — 同じパーティション上の無関係なアプリケーションが静かにスペースを埋めている

修正アプローチ

A. WAL の肥大化をクリアする(最も一般的な原因)

古いレプリケーションスロットが最も気づきにくい原因です。PostgreSQL はすべてのスロットがそれを消費するまで、すべての WAL セグメント(各 16 MB)を保持します — たとえレプリカが数週間前に消えていても。1 つの死んだスロットが静かにギガバイト単位で蓄積することがあります。まず確認してください:

-- レプリケーションスロットとその遅延量を確認
SELECT slot_name, active, restart_lsn,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag
FROM pg_replication_slots;

ギガバイト単位の遅延があり active = false のスロットが問題です。削除してください:

-- これを削除すると PostgreSQL が保持している WAL セグメントをリサイクルできる
SELECT pg_drop_replication_slot('slot_name');

PostgreSQL は次のチェックポイントで WAL をリサイクルします。急いでいる場合は即座に強制実行してください:

CHECKPOINT;

B. 古いログを削除する

# 3日以上前のログを削除
sudo find /var/log/postgresql -name '*.log' -mtime +3 -delete

# 現在のログが巨大な場合はトランケート
# (削除しないこと — postgres がまだファイルを開いている)
sudo truncate -s 0 /var/log/postgresql/postgresql-$(date +%Y-%m-%d)*.log

C. 一時ファイルを引き起こしているクエリを強制終了する

肥大化した pgsql_tmp ディレクトリは、ほぼ常に 1 つのクエリがディスク上で大規模なソートまたはハッシュ結合を行っていることを意味します。強制終了してください — クエリが終了すると PostgreSQL が一時ファイルを自動的にクリーンアップします:

-- 最も長く実行中のクエリを見つける
SELECT pid, now() - query_start AS duration, state, query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY duration DESC
LIMIT 10;

-- 問題のクエリを pid で終了させる
SELECT pg_terminate_backend(12345);

D. OS 側のスペースを確保する

# パーティション上の大きなファイルを探す
sudo find / -xdev -size +100M -printf '%s %p\n' 2>/dev/null | sort -rn | head -20

# パッケージマネージャーのキャッシュをクリア
sudo apt-get clean        # Debian/Ubuntu
sudo yum clean all        # CentOS/RHEL

# systemd ジャーナルをトリム(忙しいサーバーでは静かにギガバイト単位で増大する)
sudo journalctl --vacuum-size=200M

E. 緊急対処: ディスクを拡張するか WAL ディレクトリを移動する

削除してもスペースを十分に確保できない場合があります。容量を追加するしか方法がありません:

# オプション 1: クラウドディスク(AWS EBS、GCP PD など)をリサイズしてファイルシステムを拡張
# クラウドコンソールでリサイズした後:
sudo resize2fs /dev/xvda1    # ext4
sudo xfs_growfs /            # xfs

# オプション 2: pg_wal を空き容量のある別のパーティションに移動
sudo systemctl stop postgresql
sudo mv /var/lib/postgresql/14/main/pg_wal /mnt/extra_disk/pg_wal
sudo ln -s /mnt/extra_disk/pg_wal /var/lib/postgresql/14/main/pg_wal
sudo chown -R postgres:postgres /mnt/extra_disk/pg_wal
sudo systemctl start postgresql

F. 長期対策: テーブルの肥大化をクリーンアップする

autovacuum が追いつけない場合にデッドタプルが積み重なります — 高い書き込み負荷、クリーンアップをブロックする長時間実行トランザクション、またはデフォルトの autovacuum しきい値に対してテーブルが大きすぎる場合です。まず最も問題のあるテーブルを見つけてください:

-- デッドタプルが最も多いテーブルを探す
SELECT schemaname, tablename,
       pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size,
       n_dead_tup
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;

-- 通常の VACUUM: スペースを再利用としてマーク、テーブルロックなし、OS にスペースを返さない
VACUUM tablename;

-- VACUUM FULL: OS にスペースを返すが、テーブルをロックして完全に書き直す
-- 本番環境では慎重に使用すること
VACUUM FULL tablename;

修正を確認する

# スペースが解放されたことを確認
df -h /var/lib/postgresql

# PostgreSQL が正常かを確認
sudo systemctl status postgresql

# エラーがないことを確認するための簡単な書き込みテスト
psql -U postgres -c "CREATE TEMP TABLE _test (id int); INSERT INTO _test VALUES (1); DROP TABLE _test;"

# 残存エラーがないか最近のログをスキャン
sudo tail -50 /var/log/postgresql/postgresql-$(date +%Y-%m-%d)*.log

再発防止策

  • ディスク使用率 80% でアラートを設定してください — 100% に達した時点ですでに危機的状況です。
  • レプリケーションスロットを使用していない場合は WAL の保持量を制限してください:postgresql.confwal_keep_size = 1GB(PG13+)または wal_keep_segments = 64(PG12)を設定します。
  • postgresql.conflog_temp_files = 64MB を有効にしてください — 64 MB 以上の一時データを書き込むクエリがすべてログに記録されるため、障害が発生する前にディスクを大量消費するクエリを検出できます。
  • work_mem を適切に管理してください。デフォルトはソート操作あたり 4 MB です。256 MB に増やすのは魅力的に見えますが、それぞれ複数のソートを実行する 20 の並列クエリが数分でギガバイト単位の一時スペースを消費する可能性があります。
  • autovacuum が実行中でブロックされていないことを確認してください — 1 つの長時間実行トランザクションが、それが触れるすべてのテーブルのクリーンアップを妨げる可能性があります。

Related Error Notes