[譯] 10. 基礎備份和時間點恢復
[譯] 10. 基礎備份和時間點恢復
原文地址:https://www.interdb.jp/pg/pgsql10.html
原文作者:Hironobu SUZUKI
線上資料庫備份大致可以分為兩類:邏輯備份和物理備份。兩者各有優缺點,但邏輯備份有一個缺點;為它的效能花費太多時間。尤其是備份一個大型的資料庫需要非常長的時間,而從備份資料中恢復也需要很多時間。相反,物理備份使得在相對較短的時間內備份和恢復大型資料庫成為可能,因此在實際系統中它是一個非常重要和有用的特性。
在 PostgreSQL 中,從 8.0 版本開始提供線上物理完整備份,執行中的整個資料庫集簇(即物理備份資料)的快照稱為基礎備份(base backup
(Point-in-Time Recovery (PITR))時間點恢復也從8.0版本起可用,它使用一個基礎備份和由連續歸檔功能建立的歸檔日誌將資料庫集簇恢復到任意時間點的功能。例如,即使你犯了一個嚴重的錯誤(例如:截斷所有表),此功能能讓你將資料庫恢復到犯錯前的時刻。
在本章將描述以下主題:
- 基礎備份是什麼
- PITR的工作原理
- timelineId 是什麼
- 時間線歷史檔案是什麼
在7.4或更早版本,PostgreSQL 僅支援邏輯備份(邏輯完整或部分備份和資料匯出)。
10.1 基礎備份(base backup)
首先,使用低階(low-level)命令進行基礎備份的標準流程如下所示:
(1) 發起 pg_start_backup 命令
(2) 使用想要使用的歸檔命令對資料庫集簇建立一個快照
(3) 發起 pg_stop_backup 命令
Fig. 10.1. Making a base backup.
這個簡單流程對於資料庫系統管理員來說很容易使用,因為它不需要特殊的工具,使用常用工具,如scp命令或類似的歸檔工具去建立一個基礎備份。另外,在此過程中,不需要鎖表且備份操作不會影響所有使用者發查詢請求。和其它主要的開源RDBMS相比,這是最大的優勢。
使用 pg_basebackup 實用程式是一個更簡單的建立基礎備份方法,但它在其內部實際是呼叫低階命令。
由於這對低階命令是清晰理解PITR的關鍵點之一,於是將在以下小節中探討它們。
10.1.1 pg_start_backup
pg_start_backup 準備進行基本備份。如第 9.8 節所述,恢復過程從重做(REDO)點開始,因此 pg_start_backup 必須執行檢查點以在開始進行基本備份時顯式建立重做(REDO)點。此外,其檢查點的檢查點位置必須儲存在 pg_control 以外的檔案中,因為在備份期間可能會多次執行常規檢查點。因此 pg_start_backup 執行以下四個操作:
- 強制進入整頁(full-page)寫模式
- 切換當前的WAL段檔案(8.4或更高版本)
- 執行檢查點
- 建立一個備份標籤(backup_label)檔案——該檔案在基目錄(base directory)的頂層目錄中建立,它包含有關基礎備份本身的基本資訊,如檢查點位置。
第三,四個操作是此命令的核心;執行第一、二個操作是為了更加可靠地恢復資料集簇。
備份標籤檔案包含以下六項(11或更高版本是七項):
- 檢查點位置(CHECKPOINT LOCATION):這是已記錄的由此命令建立的檢查點的LSN位置
- 開始WAL位置(START WAL LOCATION):他不與PITR一起使用,而是和第11章描述的流複製一起使用。它被命名為'START WAL LOCATION' 是因為處於複製模式(replication-mode)的備用伺服器在初始化啟動時僅讀取一次該值。
- 備份方法(BACKUP METHOD):用於製作基礎備份的方法('pg_start_backup' 或 'pg_basebackup')
- 備份源(BACKUP FROM):顯示此備份來自主還是備
- 開始時間(START TIME):pg_start_backup 執行的時間戳
- 標籤(LABEL):在 pg_start_backup 中指定的標籤
- 開始時間線(START TIMELINE):備份開始的時間線。它是在11版本中引入的用於完整性檢查。
backup_label(備份標籤)
下面顯示了9.6版本中備份標籤檔案的真例項子:
postgres> cat /usr/local/pgsql/data/backup_label START WAL LOCATION: 0/9000028 (file 000000010000000000000009) CHECKPOINT LOCATION: 0/9000060 BACKUP METHOD: pg_start_backup BACKUP FROM: master START TIME: 2022-3-27 11:45:19 GMT LABEL: Weekly Backup
正如您想象的一樣,當使用此基礎備份恢復資料庫時,PostgreSQL 從備份標籤檔案中提取'CHECKPOINT LOCATION' 用於從相應的歸檔日誌中讀取檢查點記錄,然後從該檢查點記錄中獲取重做(REDO)點並啟動恢復程序。(詳細內容將在下一節中描述)
10.1.2 pg_stop_backup
pg_stop_backup 執行以下五個操作來完成備份。
- 如果在pg_start_backup中強制進行了更改整頁寫模式,則重置為非整頁(non-full-page)寫入模式
- 寫入一條備份端的XLOG記錄
- 切換WAL段檔案
- 建立備份歷史檔案——該檔案包含備份標籤檔案的內容和pg_stop_backup 已執行的時間戳
- 刪除備份標籤檔案——從基礎備份恢復需要備份標籤檔案,一旦複製,在原始資料庫集簇中就不再需要了。
備份歷史檔案的命名方式如下所示。
{WAL segment}.{offset value at the time the base backup was started}.backup
10.2 時間點恢復(PITR)
圖10.2 顯示了PITR的基本概念。PITR 模式中的 PostgreSQL 在基礎備份上重放存檔日誌的 WAL 資料,從 pg_start_backup 建立的重做(REDO)點到你想恢復的點。在PostgreSQL 中,恢復的點稱為恢復目標(recovery target)。
Fig. 10.2. Basic concept of PITR.
這裡描述PITR的工作原理。假設您在2022年3月27日 12:05 GMT 執行了一個誤操作。您應該刪除資料庫集簇並使用之前建立的基礎備份恢復一個新的集簇。然後,在recovery.conf(11或更早版本)或 postgresql.conf (12或更高版本)中設定restore_command引數的命令,並設定 recovery_target_time 的時間為誤操作的時間點(此例為12:05 GMT )。
# Place archive logs under /mnt/server/archivedir directory.
restore_command = 'cp /mnt/server/archivedir/%f %p'
recovery_target_time = "2022-3-27 12:05 GMT"
當 PostgreSQL 啟動時,如果資料庫集簇中有 recovery.conf(11 或更早版本)或 recovery.signal(12 或更高版本)和 備份標籤檔案,則進入 PITR 模式。
recovery.conf
recovery.conf 已在12版本中棄用,所有與複製相關(recovery-related) 的引數都應該寫入到postgresql.conf中。詳細看官方文件
在版本 12 或更高版本中,當您從基本備份恢復伺服器時,您需要在資料庫集簇目錄中建立一個名為 recovery.signal 的空檔案。
$ touch /usr/local/pgsql/data/recovery.signal
PITR 過程與第 9 章中描述的正常恢復過程幾乎相同;它們之間的唯一區別是以下兩點:
- WAL段/歸檔日誌從哪裡讀取?
- 正常恢復模式(Normal recovery mode)—— 來自基目錄下的pg_xlog子目錄(在10或更高版本pg_wal子目錄)
- PITR模式—— 來自配置引數 archive_command 指定的歸檔目錄
- 檢查點位置從哪裡讀取?
- 正常恢復模式(Normal recovery mode)—— 來自pg_control檔案
- PITR模式 —— 來自備份標籤檔案
PITR流程的概要描述如下:
(1) 為了找到重做(REDO)點,PostgreSQL 通過內建函式read_backup_label從備份標籤檔案中讀取'CHECKPOINT LOCATION'的值。
(2) PostgreSQL 從recovery.conf(11或更早版本)或 postgresql.conf (12或更高版本)中讀取一些引數值;在此例中,restore_command 和 recovery_target_time。
(3) PostgreSQL 從由'CHECKPOINT LOCATION'的值中輕易獲得的重做(REDO)點開始重放WAL資料。從歸檔日誌中讀取WAL資料,它們是通過執行寫入到 restore_command 引數的命令將歸檔目錄中歸檔日誌複製到臨時目錄。(臨時目錄複製過來的日誌檔案在使用後被刪除)
在此例中,PostgreSQL 從重做(REDO)點開始讀取並重放WAL資料到時間戳“2022-3-27 12:05:00”之前的哪個歸檔日誌,因為recovery_target_time 引數設定為該時間戳。如果 recovery.conf(11 或更早版本)或 recovery.signal(12 或更高版本)檔案未設定恢復目標,PostgreSQL 將重放到歸檔日誌的末端。
(4) 完成恢復程序後,在pg_xlog子目錄(在10或更高版本為pg_wal子目錄)建立一個時間線歷史檔案(timeline history file),如:'00000002.history';如果開啟了日誌歸檔功能,還會在歸檔目錄下建立同名檔案。該檔案的內容和作用將在下面章節中描述。
提交和中止操作的記錄包含每個操作完成的時間戳(兩種操作的 XLOG 資料部分分別在 xl_xact_commit 和 xl_xact_abort 中定義)。因此,如果為引數 recovery_target_time設定目標時間,每當重放提交或中止操作的 XLOG 記錄時,PostgreSQL 可以選擇是否繼續恢復。當重放XLOG記錄中的每個操作時,PostgreSQL比較目標時間和記錄中寫入的每個時間戳;如果時間戳超過目標時間,則完成 PITR 過程。
read_backup_label 函式定義在 src/backend/access/transam/xlog.c
xl_xact_commit 和 xl_xact_abort 結構體定義在 src/include/access/xact.h
為什麼我們可以使用常用的歸檔工具來進行基礎備份?
恢復過程是使資料庫集簇處於一致狀態的過程,儘管集簇是不一致的。由於 PITR 基於恢復過程,即使基礎備份是一堆不一致的檔案,它也可以恢復資料庫集簇。這就是我們可以在沒有檔案系統快照功能或專用工具的情況下使用常用歸檔工具的原因。
10.3 時間線ID( timelineId )和時間線歷史檔案(timeline history file)
PostgreSQL 中的 Timeline 用於區分源資料庫集簇和恢復的資料庫集簇,是 PITR 的核心概念。在本節中,描述了與時間線相關的兩件事:timelineId 和時間線歷史檔案(timeline history files)。
10.3.1 timelineId
每個時間線都有一個對應的時間線 ID(timelineId),一個從 1 開始的 4 位元組無符號整數。
為每個資料庫集簇分配一個單獨的時間線 ID。initdb實用程式建立的源端資料庫集簇的timelineId為1。每當資料庫集簇恢復時,timelineId都會增加1。比如上一節的例子,從源端恢復的集簇的timelineId是2。
圖 10.3 從timelineId的角度說明了 PITR 過程。首先,我們移除當前的資料庫集簇,然後用先前做的基礎備份恢復,以便回到恢復的起點,這種情況如圖中紅色箭頭曲線所示。接下來,我們啟動 PostgreSQL 伺服器,該伺服器從 pg_start_backup 建立的重做(REDO)點開始重放歸檔日誌中的 WAL 資料,通過沿著初始時間線(timelineId 1)進行跟蹤,直到恢復目標,這種情況在圖中的藍色箭頭線表示。然後,一個新的timelineId 2 被分配給恢復的資料庫集簇,PostgreSQL 在新的時間線上執行。
Fig. 10.3. Relation of timelineId between an original and a recovered database clusters.
正如第 9 章中簡要提到的,WAL 段檔名的前 8 位等於每個段建立的資料庫集簇的時間線 ID。當timelineId改變時,WAL段檔名也會改變。
重點關注 WAL 段檔案,將再次描述恢復過程。假設我們使用兩個歸檔日誌 '000000010000000000000009' 和 '00000001000000000000000A' 來恢復資料庫集簇。新恢復的資料庫集簇分配了timelineId 2,PostgreSQL 從'00000002000000000000000A' 開始建立WAL段。圖 10.4 顯示了這種情況。
Fig. 10.4. Relation of WAL segment files between an original and a recovered database clusters.
10.3.2 時間線歷史檔案
完成恢復程序後,在pg_xlog子目錄下(在10或更高版本為pg_wal子目錄)建立一個名為'00000002.history'的時間線歷史檔案。該檔案記錄了時間線分支來自哪裡和時間線。
該檔案的命名規則如下顯示:
"8-digit new timelineId".history
每個時間線歷史檔案包含至少一行,每行由下面的三項組成:
- timelineId - 用於恢復的歸檔日誌的時間線ID
- LSN - WAL段檔案發生切換時的LSN位置
- reason - 對時間線更改原因的可讀解釋
一個具體的示例如下所示:
postgres> cat /home/postgres/archivelogs/00000002.history
1 0/A000198 before 2022-3-27 12:05:00.861324+00
含義如下:
資料庫集簇(timelineId =2) 是基於timelineId為1的基礎備份,通過歸檔日誌重放直到0/A000198恢復到'2022-3-27 12:05:00.861324+00'之前的時間點。
這樣,每個時間線歷史檔案告訴我們恢復資料庫集簇的完成歷史。此外,它也用於 PITR 過程本身。詳細內容將在下一節中解釋。
在9.3版本改變了時間線歷史檔案的格式。下面顯示了9.3版本之前和之後的格式,但沒有詳細介紹
9.3後
timelineId LSN "reason"
直到9.2版本
timelineId WAL_segment "reason"
10.4 使用時間線歷史檔案進行時間點恢復
時間線歷史檔案在第二個和後續的 PITR 過程中起著重要作用。通過嘗試第二次恢復,我們將探索它是如何使用的。
再次,假設您在 12:15:00 在timelineId 為2 的恢復資料庫集簇中執行了一個誤操作。在這種情況下,要恢復資料庫集簇,您應該建立一個新的recovery.conf,如下所示:
restore_command = 'cp /mnt/server/archivedir/%f %p'
recovery_target_time = "2022-3-27 12:15:00 GMT"
recovery_target_timeline = 2
引數 recovery_target_time 設定為誤錯誤的時刻,recovery_target_timeline 設定為'2',以便沿著這條時間線恢復。
重啟PostgreSQL伺服器以便進入PITR模式,沿著timelineId 2 恢復資料庫直到目標時間。見圖10.5。
Fig. 10.5. Recover the database at 12:15:00 along the timelineId 2.
(1) PostgreSQL 從備份標籤檔案中讀取'CHECKPOINT LOCATION'的值
(2) 從 recovery.conf 中讀取一些引數值;在此示例中的 restore_command、recovery_target_time 和 recovery_target_timeline
(3) PostgreSQL讀取引數recovery_target_timeline的值對應的時間線歷史檔案'00000002.history'
(4) PostgreSQL 通過以下步驟重放 WAL 資料:
- 從重做(REDO)點到寫入00000002.history檔案的LSN '0/A000198',PostgreSQL讀取並重放timelineId為1的相應歸檔日誌的WAL資料
- 從LSN '0/A000198'之後到時間戳'2022-3-27 12:15:00'之前,PostgreSQL讀取並重放timelineId為2的WAL資料(相應歸檔日誌)
(5) 完成恢復程序後,當前的timelineId會推進到3,在pg_xlog子目錄下(在10或更高版本為pg_wal子目錄)和歸檔目錄下建立一個名為'00000003.history'的新時間線歷史檔案。
```sh
postgres> cat /home/postgres/archivelogs/00000003.history
1 0/A000198 before 2022-3-27 12:05:00.861324+00
2 0/B000078 before 2022-3-27 12:15:00.927133+00
```
當您多次執行 PITR 時,您應該明確設定一個時間線 ID 以使用適當的時間線歷史檔案。
這樣,時間線歷史檔案不僅是資料庫集簇的歷史日誌,還是PITR過程的恢復說明檔案。