2025年9月25日: PostgreSQL 18 釋出!
支援的版本:當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0

26.4. 熱備 #

熱備(hot standby)是指在伺服器處於歸檔恢復或備用模式時,能夠連線到伺服器並執行只讀查詢的能力。這對於複製以及精確地將備份恢復到所需狀態都非常有用。“熱備”這個術語也指伺服器能夠從恢復模式過渡到正常執行模式,而使用者可以繼續執行查詢和/或保持連線開啟。

在熱備模式下執行查詢與正常查詢操作類似,但存在一些使用和管理上的差異,下文將對此進行解釋。

26.4.1. 使用者視角 #

當備用伺服器上的 hot_standby 引數設定為 true 時,一旦恢復將系統帶入一致狀態,備用伺服器就會開始接受連線併為熱備做好準備。所有此類連線都嚴格為只讀;即使是臨時表也不能寫入。

備用伺服器上的資料需要一些時間才能從主伺服器到達,因此主伺服器和備用伺服器之間會存在可衡量的延遲。因此,在主伺服器和備用伺服器上幾乎同時執行相同的查詢可能會返回不同的結果。我們稱備用伺服器上的資料與主伺服器是最終一致的。一旦主伺服器上某個事務的提交記錄在備用伺服器上重放,該事務所做的更改對在備用伺服器上獲取的任何新快照都可見。快照可能在每個查詢開始時或每個事務開始時獲取,具體取決於當前的事務隔離級別。有關更多詳細資訊,請參見第 13.2 節

在熱備期間開始的事務可以發出以下命令:

  • 查詢訪問:SELECT, COPY TO

  • 遊標命令:DECLARE, FETCH, CLOSE

  • 設定:SHOW, SET, RESET

  • 事務管理命令

    • BEGIN, END, ABORT, START TRANSACTION

    • SAVEPOINT, RELEASE, ROLLBACK TO SAVEPOINT

    • EXCEPTION 塊和其他內部子事務

  • LOCK TABLE,但僅限於以下模式:ACCESS SHARE, ROW SHAREROW EXCLUSIVE

  • 計劃和資源:PREPARE, EXECUTE, DEALLOCATE, DISCARD

  • 外掛和擴充套件:LOAD

  • UNLISTEN

在熱備期間開始的事務永遠不會被分配事務 ID,也不會寫入系統預寫日誌 (WAL)。因此,以下操作會產生錯誤訊息:

  • 資料操作語言 (DML):INSERT, UPDATE, DELETE, MERGE, COPY FROM, TRUNCATE。請注意,不允許執行會導致觸發器在恢復期間被執行的操作。此限制甚至適用於臨時表,因為在沒有分配事務 ID 的情況下無法讀取或寫入錶行,而這在當前熱備環境中是不可能的。

  • 資料定義語言 (DDL):CREATE, DROP, ALTER, COMMENT。此限制甚至適用於臨時表,因為執行這些操作需要更新系統目錄表。

  • SELECT ... FOR SHARE | UPDATE,因為在不更新底層資料檔案的情況下無法獲取行鎖。

  • 生成 DML 命令的 SELECT 語句上的規則。

  • LOCK,它明確請求的模式高於 ROW EXCLUSIVE MODE

  • LOCK 採用簡短的預設形式,因為它請求 ACCESS EXCLUSIVE MODE

  • 明確設定非只讀狀態的事務管理命令

    • BEGIN READ WRITE, START TRANSACTION READ WRITE

    • SET TRANSACTION READ WRITE, SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE

    • SET transaction_read_only = off

  • 兩階段提交命令:PREPARE TRANSACTION, COMMIT PREPARED, ROLLBACK PREPARED,因為即使是隻讀事務也需要在準備階段(兩階段提交的第一階段)寫入 WAL。

  • 序列更新:nextval(), setval()

  • LISTEN, NOTIFY

在正常操作中,只讀事務允許使用 LISTENNOTIFY,因此熱備會話的操作比普通只讀會話受到更嚴格的限制。未來版本可能會放寬其中一些限制。

在熱備期間,transaction_read_only 引數始終為 true,且無法更改。但是,只要不嘗試修改資料庫,熱備期間的連線將與任何其他資料庫連線一樣工作。如果發生故障轉移或切換,資料庫將切換到正常處理模式。會話在伺服器更改模式期間將保持連線。一旦熱備完成,就可以發起讀寫事務(即使是從熱備期間開始的會話)。

使用者可以透過發出 SHOW in_hot_standby 來確定其會話當前是否處於熱備狀態。(在 14 版本之前的伺服器版本中,in_hot_standby 引數不存在;對於舊伺服器,一個可行的替代方法是 SHOW transaction_read_only。)此外,一組函式(表 9.98)允許使用者訪問有關備用伺服器的資訊。這些允許您編寫了解資料庫當前狀態的程式。這些程式可用於監視恢復進度,或允許您編寫將資料庫恢復到特定狀態的複雜程式。

26.4.2. 處理查詢衝突 #

主伺服器和備用伺服器在很多方面是鬆散連線的。主伺服器上的操作會影響備用伺服器。因此,它們之間存在負面互動或衝突的可能性。最容易理解的衝突是效能:如果主伺服器正在進行大量資料載入,那麼它將在備用伺服器上生成類似的 WAL 記錄流,因此備用查詢可能會爭用系統資源,例如 I/O。

此外,熱備還可能發生其他型別的衝突。這些衝突是硬衝突,意味著查詢可能需要被取消,在某些情況下,會話也可能被斷開以解決它們。使用者可以通過幾種方式來處理這些衝突。衝突的案例包括:

  • 在主伺服器上獲取的訪問排他鎖,包括顯式的 LOCK 命令和各種DDL操作,與備用查詢中的表訪問發生衝突。

  • 在主伺服器上刪除表空間會與在備用伺服器上使用該表空間進行臨時工作檔案的備用查詢發生衝突。

  • 在主伺服器上刪除資料庫會與連線到備用伺服器上該資料庫的會話發生衝突。

  • 從 WAL 應用的清理記錄與仍然可以看到要刪除的任何行的備用事務發生衝突。

  • 從 WAL 應用的清理記錄與訪問備用伺服器上目標頁面的查詢發生衝突,無論要刪除的資料是否可見。

在主伺服器上,這些情況只會導致等待;使用者可以選擇取消其中一個衝突的操作。然而,在備用伺服器上沒有選擇:WAL 日誌記錄的操作已經在主伺服器上發生,所以備用伺服器不得未能應用它。此外,允許 WAL 應用無限期等待可能非常不受歡迎,因為備用伺服器的狀態將與主伺服器越來越不同步。因此,提供了一種機制來強制取消與待應用 WAL 記錄衝突的備用查詢。

問題場景的一個例子是,主伺服器上的管理員在備用伺服器上正在查詢的表上執行 DROP TABLE。顯然,如果 DROP TABLE 在備用伺服器上執行,則備用查詢無法繼續。如果這種情況發生在主伺服器上,DROP TABLE 將等待直到其他查詢完成。但是,當 DROP TABLE 在主伺服器上執行時,主伺服器沒有關於備用伺服器上執行哪些查詢的資訊,因此它不會等待任何此類備用查詢。WAL 更改記錄在備用查詢仍在執行時傳入備用伺服器,導致衝突。備用伺服器要麼必須延遲應用 WAL 記錄(以及之後的所有記錄),要麼取消衝突的查詢,以便可以應用 DROP TABLE

當衝突查詢很短時,通常希望透過稍加延遲 WAL 應用來讓它完成;但長時間延遲 WAL 應用通常是不希望的。因此,取消機制具有引數,max_standby_archive_delaymax_standby_streaming_delay,它們定義了 WAL 應用允許的最大延遲。一旦應用任何新接收的 WAL 資料所花費的時間超過了相關的延遲設定,衝突的查詢將被取消。有兩個引數,以便可以為從歸檔讀取 WAL 資料(即,從基礎備份進行初始恢復或趕上落後很多的備用伺服器)與透過流複製讀取 WAL 資料的情況指定不同的延遲值。

在一個主要用於高可用性的備用伺服器中,最好將延遲引數設定得相對較短,這樣伺服器就不會因為備用查詢引起的延遲而與主伺服器相差太遠。但是,如果備用伺服器用於執行長期執行的查詢,那麼較高的甚至無限的延遲值可能是可取的。但請記住,長期執行的查詢可能會導致備用伺服器上的其他會話無法看到主伺服器上的最新更改,如果它延遲了 WAL 記錄的應用。

一旦超過了 max_standby_archive_delaymax_standby_streaming_delay 指定的延遲,衝突的查詢將被取消。這通常只會導致一個取消錯誤,儘管在重放 DROP DATABASE 的情況下,整個衝突的會話將被終止。此外,如果衝突發生在某個空閒事務持有的鎖上,衝突的會話將被終止(此行為在未來版本中可能會改變)。

被取消的查詢可以立即重試(當然,是在開始新事務之後)。由於查詢取消取決於正在重放的 WAL 記錄的性質,因此被取消的查詢如果再次執行,很可能會成功。

請記住,延遲引數是與 WAL 資料被備用伺服器接收以來的經過時間進行比較的。因此,允許備用伺服器上任何一個查詢的寬限期永遠不會超過延遲引數,如果備用伺服器已經因為等待之前的查詢完成而落後,或者因為無法跟上繁重的更新負載而落後,實際時間可能會短得多。

備用查詢與 WAL 重放之間衝突的最常見原因是“早期清理”。通常,PostgreSQL 允許在沒有需要看到它們的事務時清理舊的行版本,以確保根據 MVCC 規則正確地可見資料。然而,這個規則只能應用於主伺服器上執行的事務。因此,主伺服器上的清理可能會刪除備用伺服器上事務仍然可見的行版本。

行版本清理不是與備用查詢衝突的唯一潛在原因。所有僅索引掃描(包括在備用伺服器上執行的掃描)都必須使用一個MVCC與可見性圖(visibility map)“一致”的快照。因此,當 VACUUM 將頁面設定為所有可見(all-visible)在可見性圖中 並且該頁面包含一個或多個對所有備用查詢可見的行時,就會發生衝突。因此,即使對沒有更新或刪除的行進行 VACUUM 也可能導致衝突。

使用者應該清楚,在主伺服器上定期大量更新的表將很快導致備用伺服器上長期執行的查詢被取消。在這種情況下,為 max_standby_archive_delaymax_standby_streaming_delay 設定一個有限的值可以被認為是類似於設定 statement_timeout

如果備用查詢取消的數量被發現是不可接受的,則存在補救的可能性。第一個選項是設定 hot_standby_feedback 引數,該引數可防止 VACUUM 刪除最近死亡的行,從而不會發生清理衝突。如果您這樣做,您應該注意到這將延遲主伺服器上死行的清理,這可能會導致不理想的表膨脹。但是,清理情況不會比在主伺服器上直接執行備用查詢更糟,您仍然可以獲得將執行解除安裝到備用伺服器的好處。如果備用伺服器頻繁連線和斷開連線,您可能需要進行調整以處理 hot_standby_feedback 未被提供反饋的期間。例如,考慮增加 max_standby_archive_delay,這樣在斷開連線期間,查詢就不會因 WAL 歸檔檔案中的衝突而迅速被取消。您還應該考慮增加 max_standby_streaming_delay,以避免在重新連線後被新到達的流式 WAL 條目快速取消。

可以透過查詢備用伺服器上的 pg_stat_database_conflicts 系統檢視來檢視查詢取消的數量及其原因。pg_stat_database 系統檢視也包含摘要資訊。

使用者可以透過 log_recovery_conflict_waits 引數控制 WAL 重放等待衝突的時間超過 deadlock_timeout 時是否生成日誌訊息。

26.4.3. 管理員視角 #

如果在 postgresql.conf 中設定 hot_standbyon(預設值)並且存在 standby.signal 檔案,則伺服器將以熱備模式執行。但是,熱備連線可能需要一些時間才能允許,因為伺服器在完成足夠的恢復以提供一致的狀態以供查詢執行時不會接受連線。在此期間,嘗試連線的客戶端將收到錯誤訊息被拒絕。要確認伺服器已啟動,請從應用程式嘗試迴圈連線,或檢視伺服器日誌中的這些訊息:

LOG:  entering standby mode

... then some time later ...

LOG:  consistent recovery state reached
LOG:  database system is ready to accept read-only connections

一致性資訊在主伺服器的每個檢查點記錄一次。當讀取主伺服器上 wal_level 未設定為 replicalogical 時生成的 WAL 時,無法啟用熱備。即使達到一致狀態,如果滿足以下兩個條件,恢復快照可能仍未準備好進行熱備,從而延遲接受只讀連線。要啟用熱備,主伺服器上需要關閉具有超過 64 個子事務的長生存期寫事務。

  • 一個寫事務有超過 64 個子事務

  • 非常長生存期的寫事務

如果您正在使用基於檔案的日誌傳輸(“溫備”),您可能需要等到下一個 WAL 檔案到達,這可能長達主伺服器上的 archive_timeout 設定。

一些引數的設定決定了用於跟蹤事務 ID、鎖和已準備事務的共享記憶體大小。為了確保備用伺服器在恢復期間不會耗盡共享記憶體,這些共享記憶體結構在備用伺服器上不應小於主伺服器上。例如,如果主伺服器使用了已準備事務,但備用伺服器沒有為跟蹤已準備事務分配任何共享記憶體,那麼在更改備用伺服器的配置之前,恢復將無法繼續。受影響的引數是:

  • max_connections

  • max_prepared_transactions

  • max_locks_per_transaction

  • max_wal_senders

  • max_worker_processes

要確保這不會成為問題,最簡單的方法是在備用伺服器上將這些引數設定為等於或大於主伺服器上的值。因此,如果您想增加這些值,應該先在所有備用伺服器上進行,然後再將更改應用於主伺服器。反之,如果您想減小這些值,應該先在主伺服器上進行,然後再將更改應用於所有備用伺服器。請記住,當備用伺服器被提升為主伺服器時,它將成為其下游備用伺服器的所需引數設定的新參考。因此,為了避免在切換或故障轉移期間出現此問題,建議在所有備用伺服器上保持這些設定相同。

WAL 跟蹤主伺服器上的這些引數的更改。如果熱備處理的 WAL 表明主伺服器上的當前值高於其自身的值,它將記錄一個警告並暫停恢復,例如:

WARNING:  hot standby is not possible because of insufficient parameter settings
DETAIL:  max_connections = 80 is a lower setting than on the primary server, where its value was 100.
LOG:  recovery has paused
DETAIL:  If recovery is unpaused, the server will shut down.
HINT:  You can then restart the server after making the necessary configuration changes.

此時,需要在備用伺服器上更新設定並重新啟動例項,然後才能繼續恢復。如果備用伺服器不是熱備,那麼當它遇到不相容的引數更改時,它將立即關閉而不會暫停,因為保持其執行沒有意義。

管理員選擇適合 max_standby_archive_delaymax_standby_streaming_delay 的設定非常重要。最佳選擇取決於業務優先順序。例如,如果伺服器主要任務是作為高可用性伺服器,那麼您將需要較低的延遲設定,甚至可能為零,儘管這是一個非常激進的設定。如果備用伺服器的任務是作為決策支援查詢的附加伺服器,那麼可以將最大延遲值設定為數小時,甚至 -1,表示永遠等待查詢完成。

在主伺服器上寫入的事務狀態“提示位”(hint bits)不進行 WAL 日誌記錄,因此備用伺服器上的資料很可能會在備用伺服器上重新寫入提示位。因此,即使所有使用者都是隻讀的,備用伺服器仍然會執行磁碟寫入;資料值本身沒有變化。使用者仍然會寫入大的排序臨時檔案並重新生成 relcache 資訊檔案,因此在熱備模式下,資料庫的任何部分都不是真正只讀的。另外請注意,使用 dblink 模組寫入遠端資料庫以及使用 PL 函式進行資料庫外的其他操作仍然是可能的,即使事務在本地是隻讀的。

以下型別的管理命令在恢復模式下不被接受:

  • 資料定義語言 (DDL):例如,CREATE INDEX

  • 許可權和所有權:GRANT, REVOKE, REASSIGN

  • 維護命令:ANALYZE, VACUUM, CLUSTER, REINDEX

再次請注意,其中一些命令實際上在主伺服器的“只讀”模式事務中是被允許的。

因此,您無法建立僅存在於備用伺服器上的額外索引,也無法建立僅存在於備用伺服器上的統計資訊。如果需要這些管理命令,應在主伺服器上執行,最終這些更改將傳播到備用伺服器。

pg_cancel_backend()pg_terminate_backend() 可以作用於使用者後端,但不能作用於執行恢復的啟動程序。pg_stat_activity 不將正在恢復的事務顯示為活動狀態。因此,在恢復期間 pg_prepared_xacts 始終為空。如果您希望解決不確定狀態的已準備事務,請在主伺服器上檢視 pg_prepared_xacts 併發出命令在那裡解決事務,或在恢復結束之後解決它們。

pg_locks 將正常顯示後端持有的鎖。pg_locks 還顯示了由啟動程序管理的虛擬事務,該事務擁有恢復正在重放的事務所持有的所有 AccessExclusiveLocks。請注意,啟動程序不獲取鎖來更改資料庫,因此除了 AccessExclusiveLocks 之外的鎖不會在啟動程序的 pg_locks 中顯示;它們被假定存在。

Nagios 外掛 check_pgsql 可以工作,因為它檢查的簡單資訊是存在的。check_postgres 監控指令碼也可以工作,儘管一些報告的值可能會給出不同的或令人困惑的結果。例如,上次 vacuum 時間將不會被維護,因為備用伺服器上沒有 vacuum。在主伺服器上執行的 vacuum 確實會將其更改傳送到備用伺服器。

WAL 檔案控制命令在恢復期間將不起作用,例如 pg_backup_start, pg_switch_wal 等。

動態載入的模組可以工作,包括 pg_stat_statements

廣告鎖(Advisory locks)在恢復期間正常工作,包括死鎖檢測。請注意,廣告鎖永遠不會進行 WAL 日誌記錄,因此主伺服器或備用伺服器上的廣告鎖不可能與 WAL 重放衝突。也不能在主伺服器上獲取廣告鎖並在備用伺服器上啟動類似的廣告鎖。廣告鎖僅與其獲取的伺服器相關。

基於觸發器的複製系統,如 Slony, LondisteBucardo 將根本不在備用伺服器上執行,儘管它們可以在主伺服器上愉快地執行,只要更改不傳送到備用伺服器以供應用。WAL 重放不是基於觸發器的,因此您不能從備用伺服器中繼到任何需要額外資料庫寫入或依賴觸發器使用的系統。

無法分配新的 OID,儘管某些UUID生成器可能仍然工作,只要它們不依賴於向資料庫寫入新狀態。

當前,在只讀事務期間不允許建立臨時表,因此在某些情況下,現有指令碼將無法正確執行。此限制可能會在後續版本中放寬。這既是一個 SQL 標準合規性問題,也是一個技術問題。

DROP TABLESPACE 僅在表空間為空時才能成功。一些備用使用者可能透過其 temp_tablespaces 引數積極使用該表空間。如果表空間中存在臨時檔案,則會取消所有活動查詢,以確保刪除臨時檔案,從而可以刪除表空間並繼續 WAL 重放。

在主伺服器上執行 DROP DATABASEALTER DATABASE ... SET TABLESPACE 將生成一個 WAL 條目,該條目將導致連線到備用伺服器上該資料庫的所有使用者被強制斷開連線。此操作會立即發生,無論 max_standby_streaming_delay 的設定如何。請注意,ALTER DATABASE ... RENAME 不會斷開使用者連線,這在大多數情況下不會被注意到,但在某些情況下可能會導致程式混淆,如果它以某種方式依賴於資料庫名稱。

在正常(非恢復)模式下,如果您為一個具有登入能力的角色的 DROP USERDROP ROLE,而該使用者仍然連線,則不會對連線的使用者產生任何影響——他們仍然保持連線。但是,該使用者無法重新連線。此行為在恢復模式下也適用,因此在主伺服器上執行 DROP USER 不會斷開備用伺服器上該使用者的連線。

累積統計系統在恢復期間是活動的。所有掃描、讀取、阻塞、索引使用等都會在備用伺服器上正常記錄。但是,WAL 重放不會增加關係和資料庫特定的計數器。即,重放不會增加 pg_stat_all_tables 的列(例如 n_tup_ins),也不會在 pg_statio_ 檢視中跟蹤啟動程序執行的讀寫操作,也不會增加相關的 pg_stat_database 列。

自動清理(Autovacuum)在恢復期間不活躍。它將在恢復結束時正常啟動。

檢查點程序(checkpointer process)和後臺寫入程序(background writer process)在恢復期間是活動的。檢查點程序將執行重新啟動點(restartpoints)(類似於主伺服器上的檢查點),後臺寫入程序將執行正常的塊清理活動。這可以包括更新儲存在備用伺服器上的提示位資訊。CHECKPOINT 命令在恢復期間被接受,儘管它執行的是重新啟動點而不是新的檢查點。

26.4.4. 熱備引數參考 #

前面在第 26.4.2 節第 26.4.3 節中提到了各種引數。

在主伺服器上,可以使用 wal_level 引數。max_standby_archive_delaymax_standby_streaming_delay 在主伺服器上設定無效。

在備用伺服器上,可以使用 hot_standby, max_standby_archive_delaymax_standby_streaming_delay 引數。

26.4.5. 注意事項 #

熱備存在一些限制。這些限制可能會也在未來的版本中得到修復,而且很可能會被修復。

  • 在獲取快照之前,需要對正在執行的事務有完整的瞭解。使用大量子事務(當前超過 64 個)的事務將延遲只讀連線的啟動,直到最長的寫事務完成為止。如果發生這種情況,將向伺服器日誌傳送說明性訊息。

  • 備用查詢的有效起始點在主伺服器的每個檢查點生成。如果在主伺服器處於關閉狀態時備用伺服器關閉,則可能無法重新進入熱備,直到主伺服器啟動,以便它在 WAL 日誌中生成更多的起始點。這種情況在可能發生的最常見情況下不是問題。通常,如果主伺服器關閉且不再可用,那很可能是由於嚴重故障,需要備用伺服器被轉換為作為新主伺服器執行。並且在主伺服器被有意關閉的情況下,協調確保備用伺服器順利成為新主伺服器也是標準程式。

  • 在恢復結束時,由已準備事務持有的 AccessExclusiveLocks 將需要正常數量的兩倍的鎖表條目。如果您計劃執行大量通常會獲取 AccessExclusiveLocks 的併發已準備事務,或者您計劃有一個獲取大量 AccessExclusiveLocks 的大型事務,建議您為 max_locks_per_transaction 選擇一個更大的值,可能達到主伺服器上引數值的兩倍。如果您將 max_prepared_transactions 設定為 0,則完全不需要考慮此問題。

  • 序列化事務隔離級別(Serializable transaction isolation level)在熱備中尚不可用。(有關詳細資訊,請參見第 13.2.3 節第 13.4.1 節。)在熱備模式下嘗試將事務設定為序列化隔離級別將生成一個錯誤。

提交更正

如果您在文件中看到任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用此表單報告文件問題。