📚

新卒3年目DBエンジニアが教えるDB基礎講座(Postgres編第4回:PostgreSQLにおけるバックアップとリストア)

2023/06/21に公開

今回の内容

今回はPostgreSQLのインスタンスが壊れる前に重要なバックアップの取得とその戻し、つまりリストアに関して取り扱います。
ただし、フルバックアップと増分や差分の違いなどの一般的内容は扱いません。
そういう方は別記事を読んでここに来てくれると幸いです。
**1.バックアップの種類
2.各バックアップの注意点
3.オンライン物理バックアップ
4.リストア
**

1.バックアップの種類

結論から言うとPostgreSQLのバックアップは3種類あります。
どれを使うかは以下のチャートで判断してください。

・PITRを実現させる→オンライン物理バックアップ
・PITRは望まないが、サービス停止はさせたくない→オンライン論理バックアップ
・PITRも望まないし、サービス停止もOK→オフライン物理バックアップ

とはいっても実際オフライン物理バックアップの例は実際見たことがありません。
個人開発の人ならやってるのかな??くらいです。
実務ではDBAと言われる職種の人たちはオンライン物理バックアップでやることが多いと思います。
今回もバックアップ=オンライン物理バックアップの前提で進めていきます。

2.バックアップの注意点

オフライン物理バックアップは実務では触らないと思うので飛ばします。
気になる方はご自身でどうぞ

オンライン論理バックアップの注意点
・どの形式でバックアップするかをよく考えてください。
 オンライン論理バックアップはpg_dump/pg_dumpallを使いますが、-cでtarからプレーンテキストなど様々な様式でバックアップを取ることができます。
・形式によってリストアコマンドが異なります。
プレーンテキスト→psql(pg_dumpallはこれだけ!)
それ以外→pg_restore
・pg_dumpではDB/テーブル単位でのバックアップとなり、ユーザー単位ではNGです。DBクラスタ全体を取得するのならばpg_dumpallを使用する必要があります。
・設定ファイルまではされないのでrsyncやcpを使ってください。

オンライン物理バックアップの注意点
・テーブル空間だけではなく、WALを取得します。オンライン物理バックアップで使うpg_basebackupコマンドではテーブル空間やWAL領域のバックアップを取得するオプションがあるので利用してください。(pg_rmanというコマンドもありますが、ここでは扱いません。)
https://jpn.nec.com/postgresql/technical_info/backup_recovery.html
・1プロセスで物理バックアップを取得するのでバックアップクラスタの大きさに時間が比例します。
・アーカイブログが自動削除NGなので要件に沿って不要な分はpg_archivecleanupで削除してください。

3.オンライン物理バックアップ

オンライン物理バックアップはpg_basebackupを使うとかさっきから言っていま菅、正確に言うとこのコマンドはpg_start_backuppg_stop_backupコマンドを足し合わせたものになります。
まず初めにイメージしやすいようにこのコマンドに関してそれぞれ迫っていきましょう。

pg_start_backup
このコマンド1発で以下の作業を行っています。
1.共有メモリ上のステータスを「バックアップ中」へ変更
→複数のバックアップが動かないようにロックするというイメージで大丈夫です。
 ただ、あまり使いはしませんが、pg_basebackupではこの処理はないので複数のバックアップを一気に走らせることが可能です。
2.WALをスイッチ
 →この処理により、バックアップ開始時点からのデータベースの変更が新しいWALファイルに記録されます。つまり、バックアップの完全性を保つために重要な工程となります。
3.チェックポイント(DirtyBufferをDiskに書き込む処理)を発行し、LSNという番号を保持します。
→Log Sequence Number(LSN)という一意の番号が生成され、バックアップのタイミングを特定するために使用されます。
4.LSNをもとにWALファイルを特定し、backup_labelに書き込みます。
→LSNを用いて、どのWALファイルからバックアップが始まったのかを特定します。そして、その情報をbackup_labelというファイルに記録します。これにより、リストア時に適切なWALファイルからリカバリを開始することが可能となります。
5.LSNを返却する。
→ 最後に、生成したLSNを呼び出し元に返却します。これにより、バックアップ後の処理(例えば、追加のWALログのアーカイブなど)で使用することが可能となります。

pg_stop_backup
1.共有メモリ上のステータスを戻す
2.backup_labelを読みLSNを取得
3.LSNを含むWALレコードを書き込む
4.WALをスイッチ
5.バックアップ履歴ファイルを書き出す
6.LSNを返却

WALスイッチに関して詳しく…
WAL(Write Ahead Log)スイッチングは、PostgreSQLの重要な操作の一つです。
現在書き込み中のWALファイルを「クローズ」し、新しいWALファイルを「オープン」するというプロセスを指します。
具体的には以下のような状況で行われます。

・バックアップの開始や終了時に、そのタイミングを明確に区切るため。
・システムが設定したWALファイルのサイズ(デフォルトでは16MB)を超えた時。
・特定の操作(例えば、手動でのWALスイッチ要求)があった時。

WALスイッチングが行われると、現在のWALファイルはクローズされます。
その内容はディスクに確定的に書き込まれます。
同時に新しいWALファイルがオープンされ、これ以降のトランザクションのログが新しいファイルに書き込まれます。

このスイッチングは、バックアップやリストアの正確性を保証するために重要です。
例えば、バックアップ開始時にWALスイッチングを行うと、バックアップ開始以降のデータベースの変更が新しいWALファイルに記録されるため、どのWALファイルからリストアを開始すべきかを明確に知ることができます。

4.オンライン物理バックアップのHands-on

今回はバックアップ先として/backups
アーカイブログの逃げ先として/archive
WALの逃げ先として/wal
本来バックアップは別サーバーに逃がすのがベストプラクティスです。今回はテストのためにこのような形にしています。

1.設定ファイルの変更
postgresql.confの以下の設定をこのように変更します。
・archive_log = on
・archive_command = 'cp %p /archive/%f'
・restore_command = 'cp %p /archive/%f'
2.バックアップの取得
1.最初に、データが更新されていることを確認します。

test=# SELECT COUNT(*) FROM sample_table;

2.pg_basebackupを用いてデータベースをバックアップします。以下のコマンドはPostgreSQLのデータディレクトリ全体をバックアップし、その結果を指定したディレクトリ(/backup/)に保存します。さらに、-F tオプションでtar形式でファイルを保存し、-zオプションでgzipで圧縮します。
そのあと更新してSELECTしときます

postgres@postgresnode01:~/14/main$ pg_basebackup -D /backup/ -F t -z
test=# SELECT COUNT(*) FROM sample_table;

3.データベースを停止します。

postgres@postgresnode01:~$ pg_ctl stop

4.データベースが停止したことを確認します。

postgres@postgresnode01:~$ pg_ctl status

5.Write Ahead Log(WAL)を保持しているディレクトリから別の場所(/wal/)にWALファイルをコピーします。

postgres@postgresnode01:~/14/main$ cp -r -p /var/lib/postgresql/14/main/pg_wal/* /wal/

6.データベースのメインディレクトリの内容をすべて削除します。

postgres@postgresnode01:~/14/main$ rm -rf /var/lib/postgresql/14/main/*

7.バックアップを解凍し、データベースのメインディレクトリにリストアします。

postgres@postgresnode01:/backup$ tar xvfz base.tar.gz -C /var/lib/postgresql/14/main/

8.バックアップからWALファイルをリストアします。

postgres@postgresnode01:/backup$ tar xvfz pg_wal.tar.gz -C /var/lib/postgresql/14/main/pg_wal/

9.先にコピーしておいたWALファイルを、リストアしたディレクトリにコピーします。

postgres@postgresnode01:/backup$ cp -r -p /wal/* /var/lib/postgresql/14/main/pg_wal/

10.リカバリのためのコマンドを指定します。このコマンドはpostgresql.confまたはrecovery.confに追記されるべきです。ここでは、WALファイルが格納されたアーカイブディレクトリから必要なWALファイルをコピーするcpコマンドを使用しています。

restore_command = 'cp /archive/%f %p'

11.signalファイルを作成し、起動します。

postgres@postgresnode01:~$touch /var/lib/postgresql/14/main/recovery.signal
postgres@postgresnode01:~$ pg_ctl start -D /etc/postgresql/14/main/

12.障害発生直前まで戻っていることを確認します。

test=# SELECT COUNT(*) FROM sample_table;

Discussion