想定している読者
PostgreSQL サーバーを VPS か手元のマシンで動かしていて、バックアップは毎晩の pg_dump という方に向けたキットです。それはディスクを失ったときには守ってくれます。しかし、誰かが 10:15 に WHERE 句なしで DELETE FROM orders を実行し、10:40 に気づいた場合は役に立ちません。昨夜のダンプに戻すと、12 時間さかのぼることになります。ポイントインタイムリカバリなら、10:14 に戻せます。
pgBackRest と Barman は高機能で、使いこなすには午後いっぱいの学習が要ります。小さなサーバー単体なら、このキットが短い選択肢です。bash スクリプト、アーカイブ一つ、クラスター一つで、隠れた処理はありません。
できること
- 継続的な WAL アーカイブ。 安全な形で設定します。ファイルを上書きせず、ディスクが満杯や読み取り専用のときは大きな音を立てて失敗し、アーカイブの失敗はディスクが埋まる前に検知します。
- ベースバックアップ。 取得後に
pg_verifybackupで検証し、パックして、チェックサムと突き合わせます。 - 世代管理。 残すいちばん古いベースバックアップを基準に、削除してよいものを割り出します。残したバックアップがまだ必要とする WAL を消すことはありません。
- リストア。 時刻、LSN、名前付きリストアポイント、アーカイブ済みの最新 WAL のいずれかを指定して、新しいディレクトリに戻します。タイムラインの扱いは任せられます。
- cron の監視。 最後にアーカイブされたセグメントの古さと滞留量を確認し、Telegram か Webhook でアラートを送ります。同じアラートは繰り返さず、復旧したときも知らせます。
- ドリル。 データを作り、バックアップを取り、わざとミスを起こし、サーバーを破壊して、クリーンなコンテナでリストアしたうえで、行数とチェックサムを比較します。
普段の使い方
日々のコマンドと緊急時のコマンドは、こんな形です。スクリプトはふつうの bash で、すべて読めます。
./backup-base.sh && ./prune.sh # nightly
./check-archive.sh # every 5 minutes, exit code plus alert
./verify-archive.sh # daily: is the WAL chain complete?
# the bad DELETE happened around 10:15 UTC
./restore-to-time.sh --target-dir /srv/restore/r1 \
--time "2026-11-09 10:14:59+00" --start --port 5433
psql -h /tmp -p 5433 -d postgres # look around, then copy back what you need
ほかのターゲットは、--name before_migration(自分で作ったリストアポイント)、--lsn 0/5000060、ふつうの災害復旧用の restore-latest.sh です。--pause オプションを付けると、ターゲットでリカバリが止まるので、データを読み取り専用で確認してから先へ進むか決められます。
実行を拒否するケース
作業の大半は、黙って間違えてはいけない部分に使いました。
- アーカイブの途中で WAL セグメントが欠けている。 リストアは止まり、セグメント名を示して、ゼロ以外のコードで終了します。テストでは、強制的に進めると 410 行のうち 200 行が、エラーなしで返ってきました。既定で拒否するのは、まさにこのためです。
- 最初のベースバックアップより前の時刻をターゲットにした場合、空でないデータディレクトリや稼働中のディレクトリへのリストア、root での実行。
- アーカイブ済みファイルの上書き。 リストアしたサーバーがタイムライン番号を振り直して再起動した場合も含みます。
- 残してあるベースバックアップが必要とする WAL の削除。
- テーブルスペース。 リストアできないものを作るくらいなら、バックアップスクリプトは止まります。
計測結果
ノート PC クラスの ARM64 マシンで一回実行しました。PostgreSQL は既定の設定です。
リストアにかかる時間(サーバーを破壊し、クリーンなコンテナでリストア)
| 小 | 大 | |
|---|---|---|
| データ | メインテーブルに 200,000 行、78 MB | 5,000,000 行、989 MB |
| リストア合計 | 2.4 s | 42.0 s |
| うち起動と WAL の再生 | 2.0 s | 36.1 s |
| 行数とチェックサム | 一致 | 一致 |
どこまで失う可能性があるか
毎秒 20 回の insert のあと、kill -9 して、アーカイブだけからリストアしました。「失った」は、受理されたのにあとで存在しなかったコミットのことです。
archive_timeout | 受理 | 復元 | 失った件数 |
|---|---|---|---|
| 10 s | 794 | 764 | 30(失ったコミットのうち最新のものは、復元できた状態より 1.9 s 新しい) |
| 10 s | 513 | 400 | 113(6.1 s) |
| 60 s | 1,896 | 1,175 | 721(36.0 s) |
| 0(PostgreSQL の既定) | 740 | 0 | 最後のベースバックアップ以降のすべて |
覚えておいてほしいのは、最後の行です。archive_timeout が既定のままでサーバーが静かだと、ディスクを失ったとき、最後のベースバックアップ以降がすべて失われます。ガイドでは、この設定とそのコストを説明しています。強制的にスイッチすると、圧縮しない限り、16 MiB のファイルまるごとがアーカイブされます。
オーバーヘッド
pgbench を五ラウンド交互に実行した中央値は、アーカイブなしで 5,156 tps、gzip ありで 5,112、gzip なしで 5,167 でした。差はラウンド間の 2 パーセントのばらつきの範囲内です。このマシンでは、アーカイブによるオーバーヘッドは現れませんでした。
どうテストしたか
クリーンな Debian 12 のコンテナで、PostgreSQL 15(Debian パッケージ)と PostgreSQL 17(PGDG)のそれぞれに 122 の自動チェックを実行し、さらに二つのコンテナを使った破壊とリストアのドリルも、それぞれで行いました。対象は、アーカイブの失敗が WAL の再利用を止めること、ローカルと SFTP のターゲットに対するアーカイブコマンド、ファイルを壊したベースバックアップ、すべてのリストアターゲット、新しいタイムラインでの二回目のリカバリ、世代管理、上記のすべての拒否動作です。
動作要件
Debian の PostgreSQL パッケージ構成を使った Debian 12、クラスターは一つ、bash、アラート用の curl、SFTP でアーカイブするなら openssh-client が必要です。セットアップには root、それ以外はすべて postgres ユーザーで実行します。
よくある質問
すでに pgBackRest を使っています。これは必要ですか?
必ずしも必要ではありません。pgBackRest は機能の充実したツールです。このキットは、最初から最後まで読み切れる小ささを求める場合や、pgBackRest が裏で何をしているかを理解したい場合に向いています。
テーブル単位でリストアできますか?
クラスター全体を、選んだ時点の状態で新しいディレクトリにリストアし、必要なテーブルをそこからコピーして取り出します。ドリルもまさにこの手順を使っています。
Docker やマネージド Postgres で使えますか?
いいえ。サーバーの設定とファイルへアクセスできる必要があります。マネージドサービスでは、プロバイダー自身の PITR を使ってください。
ディスクはどれくらい使いますか?
書き込み量と archive_timeout によって変わります。ガイドに、いくつかのワークロードでの WAL 量と、計測した圧縮の数値を載せています。
ライセンス
ご自身のサーバーと、勤務先のサーバーで使えます。ファイルの再販や再配布はしないでください。
変更履歴
- 0.1.0: 初回リリース。
続きを読む
- Restore PostgreSQL to a timestamp with recovery_target_time (英語)
- PostgreSQL WAL archiving from scratch on a single Debian VPS (英語)
- How much data a disk failure costs with WAL archiving (英語)
- pg_dump vs physical backup vs PITR: which to use (英語)
入手する
支払い、税、請求書は販売代行事業者の Polar が処理します。決済後にダウンロードリンクが届きます。