PITRとは、ポイント・イン・タイム・リカバリーと言って、指定した時間まで遡ってリカバリすることができます。pg_dumpだとそのときのスナップショットになるので、実際に稼働しているサービスが突然落ちた時などに、直前まで復旧することができませんが、PITRならば、直前まで復旧することができます!ただし、設定方法と復旧作業が複雑です。
本を読みながらやってみました。
基本方針
試してみたいだけなので、まずはpgbenchで作ったデータベースを対象にやってみることにしました。
- pg_start_backupを実行
- rsyncでデータベースクラスタをバックアップする
- pg_stop_backupを実行する
- データを沢山登録する
- 時間を空ける(ここまでリカバリさせるため)
- 更にデータを登録する
- dbを停止させる
- リカバリしてみる
postgresql.confを編集
まず、postgresql.confを編集しました。 archive_modeをonにすることで、WAL(Write Ahead Log)を作成させます。WALは、データ更新系のログで、DBに反映する前に作られるやつです。リカバリ時には、ベースバックアップからバックアップ時点まで戻し、そこからWALを再適用することで復旧させていきます。しかし、WALはある程度までしか保存されず、一定数になると削除されていきます。それを消される前にアーカイブするためにコマンドを指定するのが、archive_commandです。cpコマンドでWALアーカイブを保存します。本来は、データベースクラスタと異なるディスク上に保存しておくべきものですが、試したいだけなので同じディスクに置いてます。
archive_mode = on archive_command = 'cp %p /var/lib/pgsql/archivedir/%f'
設定後、postgresqlを再起動させました。設定ファイルの再読み込みだけでもよかったかも…。
pg_ctl restart
PITRやっていく
まずデータ投入
最初にデータ投入しときましょう。pgbenchでやります。デフォルトの初期化です。pgbench_accountsに10万件登録されます。
pgbench -i benchdb
pg_start_backupを実行
次にバックアップの開始のラベルを作成します。これは結構時間がかかるときもあります。
psql -c "SELECT pg_start_backup(now()::text)"
ベースバックアップを保存する
ベースバックアップは、rsyncを使うのが確実だそうです。pg_walディレクトリ以下にWALが作成されるのですが、どうせ後でコピーするので、ベースバックアップからは外します。また、postmaster.pidがあるとpostgresqlが起動しない場合など、トラブルになるかもしれないので、これも除外します。
rsync -av --delete --exclude=pg_wal/* --exclude=postmaster.pid $PGDATA/* /var/lib/pgsql/backup
pg_stop_backupを実行
ベースバックアップを取り終えたので、pg_stop_backupを実行します。
psql -c "select pg_stop_backup()"
これでバックアップ体制は出来上がってるので、データを登録していっても大丈夫なはず。
データを登録する
pgbenchで作られたテーブルに、データを登録していきます。大量にデータを登録するときに便利なgenerate_series
を使います。10万1件目から20万件登録します。
INSERT INTO pgbench_accounts (aid, bid, abalance, filler) SELECT i, 1, 0, '' FROM generate_series(100001, 200000) AS i;
これを行うことで、大量のWALが発生します。収まらなくなったWALはarchive_commandで指定したコマンドの通りにコピーされていってます。
時間を空ける
空白時間を作ります。この時間をが何時なのかを記憶しておきます。
-bash-4.2$ psql -c "select now()" now ------------------------------- 2020-08-16 17:59:10.852806+00 (1 row)
戻す時間の目安を2020-08-16 18:00:00 UTCとすることにしました。2~3分待ちます。
更にデータを登録する
20万1件目から30万件登録します。これで更にWALが作られます。
INSERT INTO pgbench_accounts (aid, bid, abalance, filler) SELECT i, 1, 0, '' FROM generate_series(200001, 300000) AS i;
postgresqlを停止する
killで止めたほうが本当の障害ぽさがあるとは思いますが、普通に止めました。
pg_ctl stop
これでリカバリを実行する条件が整いました。
リカバリする
WALをコピーする
まずは、落ち着いてWALをコピーします。WALアーカイブに移動していないものを取り出します。
cp -r ${PGDATA}/pg_wal /tmp/
元のデータベースクラスタを別名で残す
次に、元のデータベースクラスタを別名で残しておきます。消してしまうと、リカバリが失敗したときに問題の調査ができなくなるので、残しましょう。
mv ${PGDATA} ${PGDATA}.temp
ベースバックアップから復元する
rsyncでベースバックアップをデータベースクラスタを復元します。
rsync -av /var/lib/pgsql/backup/* ${PGDATA}
これでベースバックアップ時点までは戻せますが、まだWALがないので後で登録されたものが存在しない状態になります。
WALを戻す
さきほどtmpにコピーしたWALを戻します。
cp /tmp/pg_wal/* ${PGDATA}/pg_wal/
recovery.confを作成
recovery.confを作っていきます。最小限の設定は、restore_commandでWALアーカイブを取ってくる命令を書いておくことです。
vi ${PGDATA}/recovery.conf
内容は以下の通り。まずは、DBを停止させる直前まで全部戻すほうを確認します。
restore_command = 'cp /var/lib/pgsql/archivedir/%f %p'
PostgreSQLを起動
これで、後はPostgreSQLを起動すると、recovery.confがあるため、リカバリーモードで起動し、WALをWALアーカイブから順々に適用していくようになります。
pg_ctl start
これだと、全てのWALが適用されるため、pgbench_accountsには、30万件のデータが復元されます。確認してみます。
benchdb=# select aid from pgbench_accounts order by aid desc limit 5; aid -------- 300000 299999 299998 299997 299996 (5 rows)
バッチリです!
指定時刻まで復元する
ところで時間を空けておいたので、今度はそこまでの復元をやってみましょう。postgresqlを停止して、データベースクラスタを削除してまた復元作業をやります。一部はもうやってるので端折ります。
pg_ctl stop rm -rf /var/lib/pgsql/data # もうdata.tempにバックアップしてあるので先ほど復元したものは消す rsync -av /var/lib/pgsql/backup/* ${PGDATA} # もう一度ベースバックアップを持ってくる cp /tmp/pg_wal/* ${PGDATA}/pg_wal/ # もう一度WALを持ってくる vi ${PGDATA}/recovery.conf
recovery.confの内容は、以下の通り。recovery_target_timeで、適用させたいところまでの時刻を指定します。この時間までであれば、データは30万件ではなく、20万件のはずです。
restore_command = 'cp /var/lib/pgsql/archivedir/%f %p' recovery_target_time = '2020-08-16 18:00:00 UTC'
PostgreSQLを起動します。
pg_ctl start
では、psqlで確認してみます。
benchdb=# select aid from pgbench_accounts order by aid desc limit 5; aid -------- 200000 199999 199998 199997 199996 (5 rows)
20万が最後のaidになってました!完璧です!
まとめ
手順が少々複雑なので、とりあえず試しておいたほうがやっぱりいいなと思いました。 本当にPITRをやろうと思ったら、WALのディレクトリや、WALアーカイブのディレクトリは外部ストレージに保存するべきだし、バックアップも多重化する必要があるので、もっと複雑になるかと思います。でも決まった時間に戻すことができるので、オペレーションミスでうっかりデータを削除してしまっても、PITRならば、戻せます。マネージドデータベースを使えない場合はやっておく価値あるでしょう。