2023年6月2日 星期五

Postgres 備份還原

在 Postgres 有許多種全資料庫備份方式,不管是那種備分方式都會面臨一個相同問題,備分期間資料庫仍持續在運作寫入,而備分只能備走硬碟上的資料,部分仍在記憶體的資訊不會被記錄到,因此這些全備份檔案的資料並不是完整的,需要搭配備分期間產生的交易日誌檔案 WAL 才算是完整備分,這點需要特別注意。



主要有下面幾種備分方式

1. pg_basebackup

Postgres 資料庫內建備分指令,在 v15 開始支援 zstd 和 lz4 壓縮備分,注意 OS 上 libzstd 版本需要在 1.5.0 上才有支援多執行緒。

2. Snapshot 

透過 VM 或其他快照機制,需要注意在執行快照前後,要透過指令 pg_backup_start() / pg_backup_stop 來讓資料庫進入備分模式,這段期交資料庫運作仍正常,但是會在 WAL 裡面寫較完整的紀錄,才確保後續能正常復原。

3. 指令工具 pgbackrest / barman 等

基本上這些工具都是基於前面兩種方式來實作的,而指令會多一些管理功能、網路傳輸壓縮、以及像是 pgbackrest 在所有版本皆能執行多執行緒 zstd 壓縮備分,

上面這些都要搭配完整的 WAL Archive 規劃才能確保備分正常運作。


這邊僅先簡介上面這些工具,這次主要想來探討,當我們從備份檔案還原後,要怎麼進行還原。

在 Postgres 這些工具做的都僅是把檔案備分走一起還原回來對應目錄,但還有一個步驟是由資料庫本身機制處理的,那就是 WAL 的復原,如果用備分工具應該有一些選項可以幫忙指定,也可以還原檔案後手動調整設定檔案再開啟。

在 WAL 復原時,我們會需要在 PGDATA 目錄下產生 recovery.signal 這個檔案告訴資料庫我們要進行備份還原,再搭配設定檔案 postgresql.auto.conf 的設定來指定要如何還原。( v11 版本以前會寫在 recovery.conf 檔案)

1. 不做任何復原設定,預設透過 archived_wal 進行還原,會盡可能還原所有 WAL 直到找不到檔案。

2. 指定 recovery_target = 'immediate' ,當資料庫復原致一致的狀態即結束復原啟動,相當於復原至備分完成的時間點資料。

3. 指定目標還原,主要有以下選項

recovery_target_name :還原點,需要透過指令 pg_create_restore_point() 建立

recovery_target_time  : 還原到某個時間

recovery_target_xid  :還原到某筆交易後停止

recovery_target_lsn  :還原到指定 WAL 位置。

需要特別注意,從 v13 版本開始,當採用指定目標還原,但沒有足夠 WAL 紀錄還原至指定時間點時,資料庫會關閉並報錯,主會透過 last complete transaction 來判斷。

透過 pg_waldump 來檢視 WAL 檔案可以看到  txid、lsn、commit 紀錄,可以用這個來判斷擁有的 Archived WAL 的狀態,來輔助決定還原點。

enterprisedb@EDB-Standby pg_wal]$ pg_waldump /pgdata/as14/data/pg_wal/000000020000006600000000 | grep -i commit
pg_waldump: fatal: error in WAL record at 66/290: invalid record length at 66/2C8: wanted 24, got 0
rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 66/00000028, prev 65/FF002078, desc: CHECKPOINT_SHUTDOWN redo 66/28; tli 2; prev tli 2; fpw true; xid 0:284845; oid 534910; multi 1; offset 0; oldest xid 1498 in DB 15601; oldest multi 1 in DB 15602; oldest/newest commit timestamp xid: 284809/284844; oldest running xid 0; shutdown
rmgr: XLOG        len (rec/tot):     54/    54, tx:          0, lsn: 66/000000A0, prev 66/00000028, desc: PARAMETER_CHANGE max_connections=8000 max_worker_processes=8 max_wal_senders=10 max_prepared_xacts=0 max_locks_per_xact=128 wal_level=logical wal_log_hints=on track_commit_timestamp=off
rmgr: Transaction len (rec/tot):     46/    46, tx:     284845, lsn: 66/00000110, prev 66/000000D8, desc: COMMIT 2023-06-01 17:19:17.854151 CST
rmgr: Transaction len (rec/tot):     46/    46, tx:     284846, lsn: 66/00000178, prev 66/00000140, desc: COMMIT 2023-06-01 17:19:31.017399 CST
rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 66/00000218, prev 66/000001E0, desc: CHECKPOINT_ONLINE redo 66/1E0; tli 2; prev tli 2; fpw true; xid 0:284847; oid 534910; multi 1; offset 0; oldest xid 1498 in DB 15601; oldest multi 1 in DB 15602; oldest/newest commit timestamp xid: 0/0; oldest running xid 284847; online一

另外可以調整資料庫參數設定 track_commit_timestamp = on 並重啟生效,設定好後即可透過 SQL 查詢並且紀錄最後 Commit 時間。

edb=# select * from pg_last_committed_xact();

  xid   |           timestamp           | roident

--------+-------------------------------+---------

 284649 | 2023-05-19 10:55:33.275348+08 |       0

(1 row)

也可能可以考慮用指令 select txid_current(); 去定期產生紀錄,來確保在 PITR 時皆能正常還原,

一般來說災備還原會以復原到最新進度為主,因此不用額外特別設定只需要注意有 recovery.siganl 檔案即可,但如果有特殊需求會用上 PITR,因為交易的狀況可能會在復原時卡到上面那個狀況可能就需要特別注意。