Postgres Point-in-Time Recovery Kit

誤った DELETE の直前の状態に、PostgreSQL サーバーを戻せます。一晩で読めるスクリプトと、それが動くことを示すドリル付きです。

Polar で購入: $39 買い切り · v0.1.0 · Polar からすぐにダウンロード

Postgres Point-in-Time Recovery Kit のカバー画像
モックアップではなく、実際のテスト実行の出力です。

想定している読者

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 MB5,000,000 行、989 MB
リストア合計2.4 s42.0 s
うち起動と WAL の再生2.0 s36.1 s
行数とチェックサム一致一致

どこまで失う可能性があるか

毎秒 20 回の insert のあと、kill -9 して、アーカイブだけからリストアしました。「失った」は、受理されたのにあとで存在しなかったコミットのことです。

archive_timeout受理復元失った件数
10 s79476430(失ったコミットのうち最新のものは、復元できた状態より 1.9 s 新しい)
10 s513400113(6.1 s)
60 s1,8961,175721(36.0 s)
0(PostgreSQL の既定)7400最後のベースバックアップ以降のすべて

覚えておいてほしいのは、最後の行です。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: 初回リリース。

続きを読む

入手する

Polar で購入: $39

支払い、税、請求書は販売代行事業者の Polar が処理します。決済後にダウンロードリンクが届きます。