支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2

25.3. 連續歸檔和時間點回復 (PITR) #

在任何時候,PostgreSQL 都會在叢集的資料目錄的 pg_wal/ 子目錄中維護一個預寫式日誌 (WAL)。 日誌記錄對資料庫資料檔案所做的每一個變更。 此日誌存在的主要目的是為了崩潰安全:如果系統崩潰,資料庫可以透過重播自上次檢查點以來所做的日誌項目來恢復一致性。 然而,日誌的存在使得可以使用第三種策略來備份資料庫:我們可以將檔案系統層級的備份與 WAL 檔案的備份結合起來。 如果需要恢復,我們恢復檔案系統備份,然後從備份的 WAL 檔案重播以使系統達到目前狀態。 這種方法比之前的任何一種方法在管理上都更複雜,但它有一些顯著的好處。

  • 我們不需要一個完全一致的檔案系統備份作為起點。 備份中的任何內部不一致都將通過日誌重播來糾正(這與崩潰恢復期間發生的情況沒有顯著不同)。 因此,我們不需要檔案系統快照功能,只需要 tar 或類似的歸檔工具即可。

  • 由於我們可以組合一個無限長的 WAL 檔案序列來進行重播,因此只需繼續歸檔 WAL 檔案即可實現連續備份。 這對於大型資料庫尤其有價值,因為頻繁地進行完整備份可能不太方便。

  • 沒有必要將 WAL 項目一直重播到最後。 我們可以在任何時候停止重播,並獲得資料庫在該時間點的狀態的一致快照。 因此,此技術支援時間點回復:可以將資料庫恢復到自您的基本備份建立以來任何時間的狀態。

  • 如果我們將一系列 WAL 檔案持續地傳送到另一台已載入相同基本備份檔案的機器,我們就有一個暖備份系統:在任何時候我們都可以啟動第二台機器,它將擁有資料庫的幾乎最新的副本。

注意

pg_dumppg_dumpall 不會產生檔案系統層級的備份,因此不能用作連續歸檔解決方案的一部分。 這些轉儲是邏輯性的,並且不包含足夠的資訊供 WAL 重播使用。

與普通的檔案系統備份技術一樣,此方法只能支援恢復整個資料庫叢集,而不是子集。 此外,它需要大量的歸檔儲存:基本備份可能很龐大,而且繁忙的系統會產生大量的 WAL 流量,這些流量必須進行歸檔。 儘管如此,在需要高可靠性的許多情況下,它仍然是首選的備份技術。

為了使用連續歸檔(許多資料庫供應商也稱為線上備份)成功恢復,您需要一個連續的 WAL 檔案歸檔序列,該序列至少可以追溯到備份的開始時間。 因此,要開始使用,您應該在進行第一個基本備份之前設定並測試 WAL 檔案的歸檔程序。 因此,我們首先討論歸檔 WAL 檔案的機制。

25.3.1. 設定 WAL 歸檔 #

從抽象意義上講,一個正在運行的 PostgreSQL 系統會產生一個無限長的 WAL 記錄序列。 系統在物理上將此序列劃分為 WAL 段檔案,通常每個檔案為 16MB(儘管可以在 initdb 期間更改段大小)。 段檔案被賦予數字名稱,這些名稱反映了它們在抽象 WAL 序列中的位置。 當不使用 WAL 歸檔時,系統通常只建立幾個段檔案,然後透過將不再需要的段檔案重新命名為更高的段號來回收它們。 假設內容早於上次檢查點的段檔案不再重要,可以回收。

在封存 WAL 資料時,我們需要擷取每個區段檔案填滿後的內容,並在區段檔案回收再利用之前,將這些資料儲存到某處。根據應用程式和可用的硬體,可能有很多不同的方式來將資料儲存到某處:我們可以將區段檔案複製到另一台機器上透過 NFS 掛載的目錄、將它們寫入磁帶機(確保您有方法可以識別每個檔案的原始名稱),或將它們批次處理並燒錄到 CD 上,或其他完全不同的方式。為了讓資料庫管理員具有彈性,PostgreSQL 盡量不對如何進行封存做出任何假設。相反地,PostgreSQL 讓管理員指定一個 shell 命令或一個封存函式庫,以執行複製已完成的區段檔案到它需要去的地方。這可以簡單到像一個使用 cp 的 shell 命令,或者它可以調用一個複雜的 C 函數 — 這完全取決於您。

要啟用 WAL 封存,請將 wal_level 設定參數設定為 replica 或更高,將 archive_mode 設定為 on,並在 archive_command 設定參數中指定要使用的 shell 命令,或在 archive_library 設定參數中指定要使用的函式庫。實際上,這些設定通常會放在 postgresql.conf 檔案中。

archive_command 中,%p 會被要封存的檔案的路徑名稱取代,而 %f 只會被檔案名稱取代。(路徑名稱相對於目前的工作目錄,也就是叢集的資料目錄。)如果您需要在命令中嵌入實際的 % 字元,請使用 %%。最簡單有用的命令類似於

archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'  # Unix
archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"'  # Windows

這會將可封存的 WAL 區段複製到目錄 /mnt/server/archivedir。(這是一個範例,而不是建議,並且可能無法在所有平台上運作。)在 %p%f 參數被取代後,實際執行的命令可能看起來像這樣

test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp pg_wal/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065

對於每個要封存的新檔案,都會產生類似的命令。

封存命令將以與執行 PostgreSQL 伺服器的使用者相同的擁有權執行。由於要封存的 WAL 檔案系列實際上包含您資料庫中的所有內容,因此您需要確保封存的資料受到保護,不會被窺探;例如,封存到沒有群組或世界讀取權限的目錄。

重要的是,封存命令必須只在成功時才傳回零退出狀態。在獲得零結果後,PostgreSQL 將假設該檔案已成功封存,並將刪除或回收它。但是,非零狀態會告訴 PostgreSQL 該檔案未被封存;它將定期重試,直到成功為止。

另一種封存方式是使用自訂封存模組作為 archive_library。由於這些模組是用 C 編寫的,因此建立您自己的模組可能需要比編寫 shell 命令付出更多的努力。但是,封存模組可能比透過 shell 進行封存更有效能,並且它們將能夠存取許多有用的伺服器資源。有關封存模組的更多資訊,請參閱 第 49 章

當封存命令因訊號(SIGTERM 除外,該訊號用於伺服器關機的一部分)或 shell 發生錯誤且退出狀態大於 125(例如找不到命令)而終止,或者如果封存函數發出 ERRORFATAL,則封存程式會中止並由 postmaster 重新啟動。在這種情況下,失敗不會在 pg_stat_archiver 中報告。

封存命令和函式庫通常應該設計為拒絕覆寫任何預先存在的封存檔案。這是一個重要的安全功能,可以在管理員錯誤(例如將兩個不同伺服器的輸出發送到同一個封存目錄)時保持封存的完整性。建議測試您提議的封存函式庫,以確保它不會覆寫現有檔案。

在極少數情況下,PostgreSQL 可能會嘗試重新封存先前已封存的 WAL 檔案。例如,如果系統在伺服器對封存成功進行持久記錄之前崩潰,則伺服器將在重新啟動後再次嘗試封存該檔案(前提是封存仍然啟用)。當封存命令或函式庫遇到預先存在的檔案時,如果 WAL 檔案的內容與預先存在的封存檔案相同,並且預先存在的封存檔案已完全持久儲存到儲存空間,則應分別傳回零狀態或 true。如果預先存在的檔案包含與要封存的 WAL 檔案不同的內容,則封存命令或函式庫必須分別傳回非零狀態或 false

上面的 Unix 範例命令透過包含單獨的 test 步驟來避免覆寫預先存在的封存。在某些 Unix 平台上,cp 具有諸如 -i 之類的開關,可用於以較不冗長的方式執行相同的操作,但您不應依賴這些開關,除非驗證已傳回正確的退出狀態。(特別是,當使用 -i 並且目標檔案已存在時,GNU cp 將傳回狀態零,這不是所需的行為。)

在設計您的封存設定時,請考慮如果封存命令或函式庫由於某些方面需要操作員介入或封存空間不足而反覆失敗,會發生什麼情況。例如,如果您在沒有自動更換器的情況下寫入磁帶,則可能會發生這種情況;當磁帶填滿時,在更換磁帶之前,無法封存任何其他內容。您應確保適當地報告任何錯誤情況或對人為操作員的請求,以便可以合理地快速解決這種情況。pg_wal/ 目錄將繼續填滿 WAL 區段檔案,直到情況得到解決。(如果包含 pg_wal/ 的檔案系統已滿,PostgreSQL 將執行 PANIC 關機。不會遺失任何已提交的交易,但資料庫將保持離線狀態,直到您釋放一些空間。)

只要封存命令或函式庫能夠跟上您的伺服器產生 WAL 資料的平均速率,其速度並不重要。即使封存程序稍微落後,正常操作也會繼續。如果封存嚴重落後,這將增加在發生災難時會遺失的資料量。這也意味著 pg_wal/ 目錄將包含大量尚未封存的區段檔案,這些檔案最終可能會超過可用的磁碟空間。建議您監控封存程序,以確保它按照您的意願運作。

在編寫您的封存命令或函式庫時,您應該假設要封存的檔案名稱最多可以包含 64 個字元,並且可以包含 ASCII 字母、數字和點的任意組合。沒有必要保留原始的相對路徑 (%p),但有必要保留檔案名稱 (%f)。

請注意,雖然 WAL 歸檔能讓您還原 PostgreSQL 資料庫中資料的所有修改,但它無法還原對組態檔(即 postgresql.confpg_hba.confpg_ident.conf)所做的變更,因為這些檔案是手動編輯,而不是透過 SQL 操作來進行。您可能會希望將組態檔保存在一個能透過您定期檔案系統備份程序備份的位置。關於如何重新定位組態檔,請參閱第 19.2 節

歸檔命令或函式只會在完整的 WAL 段執行。因此,如果您的伺服器產生的 WAL 流量很少(或有閒置期),則交易完成與安全地記錄在歸檔儲存區之間可能會有一段很長的延遲。為了限制未歸檔資料的舊化程度,您可以設定 archive_timeout,強制伺服器至少以該頻率切換到新的 WAL 段檔案。請注意,由於強制切換而提前歸檔的檔案,其長度仍然與完整檔案相同。因此,設定非常短的 archive_timeout 是不明智的,這會使您的歸檔儲存區膨脹。通常,archive_timeout 設定在一分鐘左右是合理的。

此外,如果您想確保剛完成的交易能盡快歸檔,您可以使用 pg_switch_wal 手動強制進行段切換。與 WAL 管理相關的其他公用函式列在表 9.95中。

wal_levelminimal 時,某些 SQL 命令會被最佳化以避免 WAL 記錄,如第 14.4.7 節中所述。如果在執行這些語句期間啟用了歸檔或串流複寫,則 WAL 將不會包含足夠的資訊來進行歸檔復原。(崩潰復原不受影響。)因此,wal_level 只能在伺服器啟動時變更。但是,archive_commandarchive_library 可以透過重新載入組態檔來變更。如果您透過 Shell 進行歸檔,並希望暫時停止歸檔,一種方法是將 archive_command 設定為空字串 ('')。這將導致 WAL 檔案累積在 pg_wal/ 中,直到重新建立有效的 archive_command 為止。

25.3.2. 製作基礎備份 #

執行基礎備份最簡單的方法是使用 pg_basebackup 工具。它可以將基礎備份建立為常規檔案或 tar 封存檔。如果需要比 pg_basebackup 所能提供的更大的彈性,您也可以使用低階 API 製作基礎備份(請參閱第 25.3.4 節)。

沒有必要擔心製作基礎備份需要花費多少時間。但是,如果您通常在禁用 full_page_writes 的情況下執行伺服器,您可能會注意到在備份運行時效能下降,因為在備份模式下會有效地強制啟用 full_page_writes

為了使用備份,您需要保留在檔案系統備份期間和之後產生的所有 WAL 段檔案。為了幫助您做到這一點,基礎備份程序會建立一個備份歷史檔案,並立即儲存到 WAL 歸檔區域。此檔案以檔案系統備份所需的第一個 WAL 段檔案命名。例如,如果起始 WAL 檔案為 0000000100001234000055CD,則備份歷史檔案將命名為類似 0000000100001234000055CD.007C9330.backup。 (檔案名稱的第二部分代表 WAL 檔案中的確切位置,通常可以忽略。)在您安全地歸檔檔案系統備份和備份期間使用的 WAL 段檔案(如備份歷史檔案中所指定)之後,所有名稱在數值上較小的已歸檔 WAL 段都不再需要用於復原檔案系統備份,並且可以刪除。但是,您應該考慮保留多個備份集,以絕對確定您可以復原您的資料。

備份歷史檔案只是一個小型文字檔案。它包含您提供給 pg_basebackup 的標籤字串,以及備份的開始和結束時間以及 WAL 段。如果您使用標籤來識別相關的傾印檔案,則已歸檔的歷史檔案足以告訴您要復原哪個傾印檔案。

由於您必須保留所有已歸檔的 WAL 檔案,直到最後一次基礎備份,因此基礎備份之間的時間間隔通常應根據您希望在已歸檔的 WAL 檔案上花費多少儲存空間來選擇。您還應該考慮如果您需要復原,您準備花費多少時間進行復原——系統必須重播所有這些 WAL 段,如果距離上次基礎備份已經很長時間,這可能需要一段時間。

25.3.3. 製作增量備份 #

您可以透過指定 --incremental 選項來使用 pg_basebackup 來進行增量備份。您必須將先前來自同一伺服器的備份清單作為 --incremental 的引數提供。在產生的備份中,非關聯檔案將完整包含,但某些關聯檔案可能會被較小的增量檔案取代,這些檔案僅包含自先前備份以來已變更的區塊,以及足夠的中繼資料來重建檔案的目前版本。

為了確定需要備份哪些區塊,伺服器使用 WAL 摘要,這些摘要儲存在資料目錄中,位於 pg_wal/summaries 目錄中。如果缺少所需的摘要檔案,則嘗試進行增量備份將會失敗。此目錄中存在的摘要必須涵蓋從先前備份的起始 LSN 到目前備份的起始 LSN 的所有 LSN。由於伺服器會在建立目前備份的起始 LSN 後立即尋找 WAL 摘要,因此必要的摘要檔案可能不會立即出現在磁碟上,但伺服器會等待任何缺少的檔案出現。如果 WAL 摘要程序已經落後,這也會有所幫助。但是,如果必要的檔案已經被移除,或者 WAL 摘要器沒有及時趕上,則增量備份將會失敗。

當復原增量備份時,不僅需要增量備份本身,還需要所有先前的備份,這些備份是提供增量備份中省略的區塊所必需的。有關此需求的更多資訊,請參閱pg_combinebackup。請注意,當叢集的檢查總和狀態已變更時,對 pg_combinebackup 的使用存在限制;請參閱pg_combinebackup 限制

請注意,使用完整備份的所有要求也適用於增量備份。 例如,您仍然需要檔案系統備份期間和之後產生的所有 WAL 區段檔案,以及任何相關的 WAL 歷史檔案。 並且您仍然需要建立一個 recovery.signal (或 standby.signal) 並執行恢復,如第 25.3.5 節所述。 在恢復時需要先前的備份可用,以及使用 pg_combinebackup 的要求,是除其他事項之外的附加要求。 請記住,PostgreSQL 沒有內建機制來判斷哪些備份仍然需要作為恢復後續增量備份的基礎。 您必須自行追蹤完整備份和增量備份之間的關係,並且務必不要移除可能在恢復後續增量備份時需要的早期備份。

增量備份通常僅對於相對較大的資料庫才有意義,這些資料庫中的大部分資料不會變更,或者僅變更緩慢。 對於小型資料庫,忽略增量備份的存在並僅進行完整備份更簡單,完整備份更易於管理。 對於所有內容都被大量修改的大型資料庫,增量備份不會比完整備份小多少。

只有在重播從比前一個依賴備份更新的檢查點開始時,才可能進行增量備份。 如果您在主節點上進行增量備份,則始終滿足此條件,因為每個備份都會觸發新的檢查點。 在備用節點上,重播從最新的重新啟動點開始。 因此,如果自上次備份以來活動很少,則備用伺服器的增量備份可能會失敗,因為可能沒有建立新的重新啟動點。

25.3.4. 使用低階 API 進行基本備份 #

您可以不使用 pg_basebackup 進行完整或增量基本備份,而是使用低階 API 進行基本備份。 此程序包含比 pg_basebackup 方法多幾個步驟,但相對簡單。 非常重要的是,這些步驟必須按順序執行,並且在繼續下一步之前,必須驗證步驟的成功。

可以同時執行多個備份 (包括使用此備份 API 啟動的備份和使用 pg_basebackup 啟動的備份)。

  1. 確保已啟用 WAL 封存並且運作正常。

  2. 以具有執行 pg_backup_start 權限的使用者身分 (超級使用者,或已被授予函數 EXECUTE 權限的使用者) 連線到伺服器 (哪個資料庫都沒關係) 並發出命令

    SELECT pg_backup_start(label => 'label', fast => false);
    

    其中 label 是您要用來唯一識別此備份作業的任何字串。 呼叫 pg_backup_start 的連線必須維持到備份結束,否則備份將自動中止。

    線上備份始終在檢查點的開始處啟動。 預設情況下,pg_backup_start 將等待下一個定期排定的檢查點完成,這可能需要很長時間 (請參閱組態參數 checkpoint_timeoutcheckpoint_completion_target)。 這通常是更可取的,因為它可以最大程度地減少對運行中系統的影響。 如果您想儘快啟動備份,請將 true 作為第二個參數傳遞給 pg_backup_start,它將請求立即檢查點,該檢查點將使用盡可能多的 I/O 儘快完成。

  3. 使用任何方便的檔案系統備份工具 (例如 tarcpio) (而不是 pg_dumppg_dumpall) 執行備份。 在您執行此操作時,既沒有必要也沒有期望停止資料庫的正常運作。 請參閱 第 25.3.4.1 節,了解在此備份期間要考慮的事項。

  4. 在與之前相同的連線中,發出命令

    SELECT * FROM pg_backup_stop(wait_for_archive => true);
    

    這會終止備份模式。 在主節點上,它也會自動切換到下一個 WAL 區段。 在備用節點上,無法自動切換 WAL 區段,因此您可能希望在主節點上執行 pg_switch_wal 以執行手動切換。 切換的原因是安排在備份間隔期間寫入的最後一個 WAL 區段檔案準備好進行封存。

    pg_backup_stop 將傳回一個包含三個值的列。 這些欄位中的第二個應寫入名為 backup_label 的檔案中,該檔案位於備份的根目錄中。 第三個欄位應寫入名為 tablespace_map 的檔案中,除非該欄位為空。 這些檔案對於備份運作至關重要,必須逐位元組寫入,不得修改,這可能需要在二進制模式下開啟檔案。

  5. 一旦備份期間活動的 WAL 區段檔案被封存,您就完成了。 由 pg_backup_stop 的第一個傳回值識別的檔案是形成完整備份檔案集所需的最後一個區段。 在主節點上,如果啟用了 archive_mode 並且 wait_for_archive 參數為 true,則 pg_backup_stop 不會傳回,直到最後一個區段已封存。 在備用節點上,archive_mode 必須為 always 才能讓 pg_backup_stop 等待。 由於您已經配置了 archive_commandarchive_library,因此這些檔案的封存會自動發生。 在大多數情況下,這會快速發生,但建議您監控您的封存系統,以確保沒有延遲。 如果由於封存命令或程式庫的失敗而導致封存程序落後,它將不斷重試,直到封存成功並且備份完成。 如果您希望對 pg_backup_stop 的執行設定時間限制,請設定適當的 statement_timeout 值,但請注意,如果 pg_backup_stop 因此終止,您的備份可能無效。

    如果備份程序監控並確保備份所需的所有 WAL 區段檔案都已成功封存,則可以將 wait_for_archive 參數 (預設為 true) 設定為 false,以便 pg_backup_stop 在停止備份記錄寫入 WAL 後立即傳回。 預設情況下,pg_backup_stop 將等待所有 WAL 都已封存,這可能需要一些時間。 必須謹慎使用此選項:如果 WAL 封存未正確監控,則備份可能不包含所有 WAL 檔案,因此將不完整且無法還原。

25.3.4.1. 備份資料目錄 #

某些檔案系統備份工具在嘗試複製檔案時,若檔案發生變更,會發出警告或錯誤訊息。當對一個活躍的資料庫進行基礎備份時,這種情況是正常的,並非錯誤。然而,您需要能夠區分這類抱怨訊息與真正的錯誤。例如,某些版本的 rsync 會針對「消失的來源檔案」傳回一個獨立的結束代碼,您可以編寫一個驅動腳本來接受這個結束代碼作為非錯誤情況。此外,某些版本的 GNU tar 如果在 tar 複製檔案時檔案被截斷,會傳回一個與致命錯誤無法區分的錯誤代碼。幸運的是,GNU tar 1.16 及更高版本在備份期間檔案被更改時,會以代碼 1 結束,而其他錯誤則以代碼 2 結束。使用 GNU tar 1.23 及更高版本,您可以使用警告選項 --warning=no-file-changed --warning=no-file-removed 來隱藏相關的警告訊息。

請務必確認您的備份包含資料庫叢集目錄下的所有檔案(例如,/usr/local/pgsql/data)。如果您使用不在該目錄下的資料表空間,請務必將它們也包含在內(並確保您的備份將符號連結存檔為連結,否則還原會損壞您的資料表空間)。

但是,您應該從備份中省略叢集的 pg_wal/ 子目錄中的檔案。這種略微的調整是有價值的,因為它可以降低還原時出錯的風險。如果 pg_wal/ 是一個指向叢集目錄外部位置的符號連結,那麼這很容易安排,這也是出於效能考量的一種常見設定方式。您可能還想排除 postmaster.pidpostmaster.opts,它們記錄了關於正在執行的 postmaster 的資訊,而不是最終將使用此備份的 postmaster。(這些檔案會讓 pg_ctl 感到困惑。)

通常最好也從備份中省略叢集的 pg_replslot/ 目錄中的檔案,以便主要伺服器上存在的複製槽不會成為備份的一部分。否則,後續使用備份來建立備援伺服器可能會導致 WAL 檔案在備援伺服器上無限期地保留,並且如果啟用 hot standby 回饋,可能會導致主要伺服器膨脹,因為使用這些複製槽的客戶端仍將連接到主要伺服器上的槽並更新它們,而不是備援伺服器。即使備份僅用於建立新的主要伺服器,複製複製槽也不太可能特別有用,因為到新的主要伺服器上線時,這些槽的內容可能已經嚴重過時。

目錄 pg_dynshmem/pg_notify/pg_serial/pg_snapshots/pg_stat_tmp/pg_subtrans/ 的內容(但不是目錄本身)可以從備份中省略,因為它們將在 postmaster 啟動時初始化。

任何以 pgsql_tmp 開頭的檔案或目錄都可以從備份中省略。這些檔案會在 postmaster 啟動時移除,並且會根據需要重新建立目錄。

每當找到名為 pg_internal.init 的檔案時,都可以將其從備份中省略。這些檔案包含關係快取資料,這些資料在還原時始終會重建。

備份標籤檔案包含您提供給 pg_backup_start 的標籤字串,以及 pg_backup_start 執行的時間和起始 WAL 檔案的名稱。如有混淆,可以查看備份檔案內部,並確定傾印檔案來自哪個備份工作階段。資料表空間對應檔案包含目錄 pg_tblspc/ 中存在的符號連結名稱以及每個符號連結的完整路徑。這些檔案不僅僅是為了您的資訊;它們的存在和內容對於系統復原過程的正確運作至關重要。

也可以在伺服器停止時進行備份。在這種情況下,您顯然無法使用 pg_backup_startpg_backup_stop,因此您需要自己追蹤哪個備份是什麼,以及相關的 WAL 檔案可以追溯到多遠。通常最好遵循上面的連續封存程序。

25.3.5. 使用連續封存備份進行還原 #

好吧,最糟糕的事情發生了,您需要從備份中還原。以下是程序

  1. 停止伺服器(如果它正在執行)。

  2. 如果您有足夠的空間,請將整個叢集資料目錄和任何資料表空間複製到臨時位置,以備日後需要。請注意,此預防措施將要求您在系統上有足夠的可用空間來容納現有資料庫的兩個副本。如果沒有足夠的空間,您至少應該保存叢集的 pg_wal 子目錄的內容,因為它可能包含系統當機前未封存的 WAL 檔案。

  3. 移除叢集資料目錄和您使用的任何資料表空間的根目錄下的所有現有檔案和子目錄。

  4. 如果您正在還原完整備份,您可以將資料庫檔案直接還原到目標目錄中。請確保它們以正確的所有權(資料庫系統使用者,而不是 root!)和正確的權限還原。如果您正在使用資料表空間,您應該驗證 pg_tblspc/ 中的符號連結是否已正確還原。

  5. 如果您正在還原增量備份,您需要將增量備份及其直接或間接依賴的所有早期備份還原到您正在執行還原的機器上。這些備份需要放置在單獨的目錄中,而不是您希望伺服器最終運行的目標目錄中。完成此操作後,使用 pg_combinebackup 從完整備份和所有後續增量備份中提取資料,並將合成完整備份寫入目標目錄。如上所述,驗證權限和資料表空間連結是否正確。

  6. 移除 pg_wal/ 中存在的任何檔案;這些檔案來自檔案系統備份,因此可能已過時,而不是最新的。如果您根本沒有封存 pg_wal/,則使用正確的權限重新建立它,並注意確保如果您之前這樣設定,則將其重新建立為符號連結。

  7. 如果您有在步驟 2 中保存的未封存的 WAL 區段檔案,請將它們複製到 pg_wal/ 中。(最好複製它們,而不是移動它們,這樣如果發生問題並且您必須重新開始,您仍然擁有未修改的檔案。)

  8. postgresql.conf 中設定還原配置設定(請參閱第 19.5.5 節),並在叢集資料目錄中建立一個檔案 recovery.signal。您可能還想暫時修改 pg_hba.conf,以防止普通使用者在您確定還原成功之前進行連線。

  9. 啟動伺服器。伺服器將進入還原模式,並開始讀取它需要的已封存 WAL 檔案。如果由於外部錯誤而終止還原,只需重新啟動伺服器,它將繼續還原。完成還原過程後,伺服器將移除 recovery.signal(以防止以後意外地重新進入還原模式),然後開始正常的資料庫操作。

  10. 檢查資料庫的內容,以確保您已還原到所需的狀態。如果沒有,請返回步驟 1。如果一切順利,請將 pg_hba.conf 恢復到正常狀態,允許您的使用者進行連線。

這一切的關鍵在於設定一個回復組態,描述您想要如何回復,以及回復應該執行到什麼程度。您絕對必須指定的一件事是 restore_command,它會告訴 PostgreSQL 如何擷取已封存的 WAL 檔案區段。與 archive_command 類似,這是一個 shell 命令字串。它可以包含 %f,它會被替換為所需的 WAL 檔案名稱;以及 %p,它會被替換為要將 WAL 檔案複製到的路徑名稱。(路徑名稱是相對於目前的工作目錄,也就是叢集的資料目錄。)如果您需要在命令中嵌入實際的 % 字元,請寫入 %%。最簡單且有用的命令如下:

restore_command = 'cp /mnt/server/archivedir/%f %p'

它會從目錄 /mnt/server/archivedir 複製先前封存的 WAL 區段。當然,您可以使用更複雜的東西,甚至是一個 shell 腳本,要求操作員掛載適當的磁帶。

重要的是,命令在失敗時傳回非零的結束狀態。系統呼叫此命令,要求不存在於封存中的檔案;在這種情況下,它必須傳回非零的值。這不是錯誤情況。一個例外是,如果命令因訊號終止(除了作為資料庫伺服器關閉的一部分使用的 SIGTERM)或 shell 發生錯誤(例如找不到命令),則回復將中止,且伺服器將不會啟動。

並非所有要求的檔案都會是 WAL 區段檔案;您也應該預期會收到後綴為 .history 的檔案要求。另請注意,%p 路徑的基本名稱會與 %f 不同;不要期望它們可以互換。

如果 WAL 區段在封存中找不到,將會在 pg_wal/ 中尋找;這允許使用最近未封存的區段。但是,可從封存取得的區段將優先於 pg_wal/ 中的檔案使用。

通常,回復將會處理所有可用的 WAL 區段,從而將資料庫還原到目前的時點(或盡可能接近給定的可用 WAL 區段)。因此,正常的回復將以 找不到檔案訊息結束,錯誤訊息的確切文字取決於您選擇的 restore_command。您也可能會在回復開始時看到一個檔案的錯誤訊息,其名稱類似於 00000001.history。這也是正常的,並且在簡單的回復情況下並不表示有問題;有關討論,請參閱 第 25.3.6 節

如果您想要回復到先前的時點(例如,在菜鳥 DBA 刪除您的主要交易表之前),只需指定所需的停止點。您可以指定停止點,稱為回復目標,可以按日期/時間、命名的還原點或完成特定的交易 ID。在撰寫本文時,只有日期/時間和命名的還原點選項非常實用,因為沒有工具可以幫助您準確識別要使用的交易 ID。

注意

停止點必須在基礎備份的結束時間之後,也就是 pg_backup_stop 的結束時間。您不能使用基礎備份來回復到該備份正在進行的時間。(要回復到這樣的時間,您必須回到您之前的基礎備份並從那裡向前回滾。)

如果回復發現損毀的 WAL 資料,回復將在該點停止,並且伺服器將不會啟動。在這種情況下,可以從頭重新執行回復過程,指定一個在損毀點之前的回復目標,以便回復可以正常完成。如果回復由於外部原因而失敗,例如系統崩潰或 WAL 封存變得無法存取,則可以簡單地重新啟動回復,它將幾乎從它失敗的地方重新開始。回復重新啟動的工作方式與正常操作中的檢查點非常相似:伺服器定期將其所有狀態強制寫入磁碟,然後更新 pg_control 檔案以指示已處理的 WAL 資料無需再次掃描。

25.3.6. 時間軸 #

將資料庫還原到先前時點的能力會產生一些複雜性,這些複雜性類似於關於時間旅行和平行宇宙的科幻故事。例如,在資料庫的原始歷史記錄中,假設您在星期二晚上 5:15 刪除了一個關鍵表,但在星期三中午才意識到您的錯誤。毫不驚慌地,您拿出您的備份,還原到星期二晚上 5:14 的時點,並啟動並執行。在資料庫宇宙的這個歷史記錄中,您從未刪除該表。但是假設您稍後意識到這不是一個好主意,並且想要回到原始歷史記錄中的星期三早上。如果您的資料庫在啟動並執行時覆蓋了一些導致您現在希望可以返回的時間的 WAL 區段檔案,您將無法做到。因此,為了避免這種情況,您需要區分在您進行時點回復後產生的 WAL 記錄系列,與在原始資料庫歷史記錄中產生的 WAL 記錄系列。

為了處理這個問題,PostgreSQL 有一個時間軸的概念。每當封存回復完成時,就會建立一個新的時間軸來識別在該回復之後產生的 WAL 記錄系列。時間軸 ID 號碼是 WAL 區段檔案名稱的一部分,因此新的時間軸不會覆蓋先前時間軸產生的 WAL 資料。例如,在 WAL 檔案名稱 0000000100001234000055CD 中,前導的 00000001 是十六進位表示的時間軸 ID。(請注意,在其他上下文中,例如伺服器日誌訊息,時間軸 ID 通常以十進位印出。)

實際上,可以封存許多不同的時間軸。雖然這看起來像是一個無用的功能,但它通常是一個救命稻草。考慮這樣一種情況,您不太確定要回復到哪個時點,因此必須透過試錯進行多次時點回復,直到找到從舊歷史記錄中分支出來的最佳位置。如果沒有時間軸,這個過程很快就會產生一個無法管理的混亂。有了時間軸,您可以回復到任何先前的狀態,包括您早期放棄的時間軸分支中的狀態。

每次建立新的時間線時,PostgreSQL 都會建立一個時間線歷程檔案,顯示該時間線從哪個時間線分支出來以及何時分支的。這些歷程檔案是必要的,以便系統在從包含多個時間線的封存檔還原時,選擇正確的 WAL 片段檔案。因此,它們會像 WAL 片段檔案一樣被封存到 WAL 封存區域。歷程檔案只是小的文字檔案,因此無限期地保存它們既便宜又合適 (不像片段檔案那麼大)。如果您願意,可以在歷程檔案中新增註解,以記錄您自己關於如何以及為何建立這個特定時間線的注意事項。當您因為實驗而擁有多個不同的時間線時,這些註解將特別有價值。

還原的預設行為是還原到封存檔中找到的最新時間線。如果您希望還原到建立基本備份時的目前時間線,或是還原到特定的子時間線 (也就是說,您希望返回到某個本身是在還原嘗試後產生的狀態),您需要在 recovery_target_timeline 中指定 current 或目標時間線 ID。您無法還原到早於基本備份的時間線所分支出來的時間線。

25.3.7. 提示與範例 #

以下提供一些設定連續封存的提示。

25.3.7.1. 獨立熱備份 #

可以使用 PostgreSQL 的備份功能來產生獨立熱備份。這些備份不能用於時間點還原,但通常比 pg_dump 轉儲檔的備份和還原速度快得多。(它們也比 pg_dump 轉儲檔大得多,因此在某些情況下,速度優勢可能會被抵消。)

與基本備份一樣,產生獨立熱備份最簡單的方法是使用 pg_basebackup 工具。如果在呼叫它時包含 -X 參數,則使用備份所需的所有預寫式日誌將自動包含在備份中,並且無需採取任何特殊操作即可還原備份。

25.3.7.2. 壓縮封存日誌 #

如果封存儲存大小是一個問題,您可以使用 gzip 來壓縮封存檔案

archive_command = 'gzip < %p > /mnt/server/archivedir/%f.gz'

然後,您需要在還原期間使用 gunzip

restore_command = 'gunzip < /mnt/server/archivedir/%f.gz > %p'

25.3.7.3. archive_command 指令碼 #

許多人選擇使用指令碼來定義他們的 archive_command,以便他們的 postgresql.conf 條目看起來非常簡單

archive_command = 'local_backup_script.sh "%p" "%f"'

任何時候您想在封存過程中使用多個命令時,都建議使用單獨的指令碼檔案。這允許在指令碼中管理所有複雜性,該指令碼可以用流行的指令碼語言 (例如 bashperl) 編寫。

可以在指令碼中解決的需求範例包括

  • 將資料複製到安全的異地資料儲存

  • 批量處理 WAL 檔案,以便每三個小時傳輸一次,而不是一次一個

  • 與其他備份和還原軟體介接

  • 與監控軟體介接以報告錯誤

提示

使用 archive_command 指令碼時,最好啟用 logging_collector。然後,從指令碼寫入到 stderr 的任何訊息都會出現在資料庫伺服器日誌中,從而可以在它們失敗時輕鬆診斷複雜的設定。

25.3.8. 注意事項 #

在撰寫本文時,連續封存技術存在一些限制。這些限制可能會在未來的版本中修復

  • 如果在建立基本備份時執行 CREATE DATABASE 命令,然後在基本備份仍在進行中時修改 CREATE DATABASE 複製的範本資料庫,則還原可能會導致這些修改也傳播到建立的資料庫中。這當然是不希望發生的。為了避免這種風險,最好在建立基本備份時不要修改任何範本資料庫。

  • CREATE TABLESPACE 命令會使用字面絕對路徑記錄到 WAL 中,因此會以相同的絕對路徑重新執行為表格空間建立。如果在不同的機器上重新執行 WAL,這可能是不希望發生的。即使在同一機器上重新執行 WAL,但重新執行到新的資料目錄中,也可能很危險:重新執行仍然會覆寫原始表格空間的內容。為了避免此類潛在的陷阱,最佳做法是在建立或刪除表格空間後建立新的基本備份。

還應該注意的是,預設的WAL格式相當龐大,因為它包含許多磁碟頁面快照。這些頁面快照旨在支援當機還原,因為我們可能需要修復部分寫入的磁碟頁面。根據您的系統硬體和軟體,部分寫入的風險可能很小,可以忽略不計,在這種情況下,您可以透過使用 full_page_writes 參數關閉頁面快照來顯著減少封存的 WAL 檔案的總量。(在這樣做之前,請閱讀 第 28 章 中的註解和警告。)關閉頁面快照不會阻止將 WAL 用於 PITR 操作。未來的開發方向是透過刪除不必要的頁面副本來壓縮封存的 WAL 資料,即使 full_page_writes 處於開啟狀態。同時,管理員可能希望透過盡可能增加檢查點間隔參數來減少 WAL 中包含的頁面快照數量。

提交更正

如果您在文件中發現任何不正確、與您使用特定功能的經驗不符或需要進一步澄清的地方,請使用 此表單報告文件問題。