閱讀140 返回首頁    go 小米 go 小米6


PostgreSQL服務器管理:高可用、負載均衡和複製

本文檔為PostgreSQL 9.6.0文檔,本轉載已得到原譯者彭煜瑋授權。

數據庫服務器可以一起工作,這樣如果主要的服務器失效則允許一個第二服務器快速接手它的任務(高可用性),或者可以允許多個計算機提供相同的數據(負載均衡)。理想情況下,數據庫服務器能夠無縫地一起工作。提供靜態網頁服務的網頁服務器可以非常容易地通過把網頁請求均衡到多個機器來組合。事實上,隻讀的數據庫服務器也可以相對容易地組合起來。不幸的是,大部分數據庫服務器收到的請求是讀/寫混合的,並且讀/寫服務器更難於組合。這是因為盡管隻讀數據隻需要在每台服務器上放置一次,但對於任意服務器的一次寫動作卻必須被傳播給所有的服務器,這樣才能保證未來對於那些服務器的讀請求能返回一致的結果。

這種同步問題是服務器一起工作的最根本的困難。因為沒有單一解決方案能夠消除該同步問題對所有用例的影響。有多種解決方案,每一種方案都以一種不同的方式提出了這個問題,並且對於一種特定的負載最小化了該問題所產生的影響。

某些方案通過隻允許一台服務器修改數據來處理同步。能修改數據的服務器被稱為讀/寫、主控或主要服務器。跟蹤主控機中改變的服務器被稱為後備或從屬服務器。如果一台後備服務器隻有被提升為一台主控服務器後才能被連接,它被稱為一台溫後備服務器,而一台總是能夠接受連接並且提供隻讀查詢的後備服務器被稱為一台熱後備服務器。

某些方案是同步的,即一個數據修改事務隻有到所有服務器都提交了該事務之後才被認為是提交成功。這保證了一次故障轉移不會丟失任何數據並且所有負載均衡的服務器將返回一致的結果(不管哪台服務器被查詢)。相反,異步的方案允許在一次提交和它被傳播到其他服務器之間有一些延遲,這產生了切換到一個備份服務器時丟失某些事務的可能性,並且負載均衡的服務器可能會返回略微陳舊的結果。當同步通信可能很慢時,可以使用異步通信。

方案也可以按照它們的粒度進行分類。某些方案隻能處理一整個數據庫服務器,而其他的允許在每個表或每個數據庫的級別上進行控製。

在任何選擇中,都必須考慮性能。通常在功能和性能之間都存在著權衡。例如,在一個低速網絡上的一種完全同步的方案可能使性能減少超過一半,而一種異步的方案產生的性能影響可能是最小的。

本節的剩餘部分勾勒了多種故障轉移、複製和負載均衡方案。其中也有一個術語表可用。

共享磁盤故障轉移

共享磁盤故障轉移避免了隻使用一份數據庫拷貝帶來的同步開銷。它使用一個由多個服務器共享的單一磁盤陣列。如果主數據庫服務器失效,後備服務器則可以掛載並啟動數據庫,就好像它從一次數據庫崩潰中恢複過來了。這是一種快速的故障轉移,並且不存在數據丟失。

共享硬件功能在網絡存儲設備中很常見。也可以使用一個網絡文件係統,但是要注意的是該文件係統應具有完全的POSIX行為(見Section 18.2.2)。這種方法的一個重大限製是如果共享磁盤陣列失效或損壞,主要和後備服務器都會變得無法工作。另一個問題是在主要服務器運行時,後備服務器永遠不能訪問共享存儲。

文件係統(塊設備)複製

共享硬件功能的一種修改版本是文件係統複製,在其中對一個文件係統的所有改變會被鏡像到位於另一台計算機上的一個文件係統。唯一的限製是該鏡像過程必須能保證後備服務器有一份該文件係統的一致的拷貝 — 特別是對後備服務器的寫入必須按照主控機上相同的順序進行。DRBD是用於 Linux 的一種流行的文件係統複製方案。

事務日誌傳送

溫備和熱備服務器能夠通過讀取一個預寫式日誌(WAL)記錄的流來保持為當前狀態。如果主服務器失效,後備服務器擁有主服務器的幾乎所有數據,並且能夠快速地被變成新的主數據庫服務器。這可以是同步的或異步的,並且隻能用於整個數據庫服務器。

可以使用基於文件的日誌傳送(Section 26.2)、流複製(見Section 26.2.5)或兩者的組合來實現一個後備服務器。關於熱備的信息可見Section 26.5。

基於觸發器的主-備複製

一個主-備複製設置會把所有數據修改查詢發送到主服務器。主服務器異步地將數據修改發送給後備服務器。當主服務器正在運行時,後備服務器可以回答隻讀查詢。後備服務器對數據倉庫查詢是一種理想的選擇。

Slony-I是這種複製類型的一個例子。它使用表粒度,並且支持多個後備服務器。因為它會異步更新後備服務器(批量),在故障轉移時可能會有數據丟失。

基於語句的複製中間件

通過基於語句的複製中間件,一個程序攔截每一個 SQL 查詢並把它發送給一個或所有服務器。每一個服務器獨立地操作。讀寫查詢必須被發送給所有服務器,這樣每一個服務器都能接收到任何修改。但隻讀查詢可以被隻發送給一個服務器,這樣允許讀負載在服務器之間分布。

如果查詢被簡單地且未經修改地廣播,random()、CURRENT_TIMESTAMP之類的函數以及序列在不同服務器上可能有不同的值。這是因為每一個服務器會獨立地操作,並且 SQL 查詢被廣播(而不是真正被修改的行)。如果這不可接受,中間件或應用必須從一個單一服務器查詢這樣的值並且然後將那些值用在寫查詢中。另一個選項是將這個複製選項和一種傳統主-備設置一起使用,即數據修改查詢隻被發送給主服務器並且通過主-備複製傳播到後備服務器,而不是通過複製中間件。必須要注意的是,所有事務要麼在所有服務器上都提交,要麼在所有服務器上都中止,也許可以使用兩階段提交(PREPARE TRANSACTION和COMMIT PREPARED)。Pgpool-II和Continuent Tungsten是這種複製類型的例子。

異步多主控機複製

對於不會被定期連接的服務器(如筆記本或遠程服務器),保持服務器間的數據一致是一個挑戰。通過使用異步的多主控機複製,每一個服務器獨立工作並且定期與其他服務器通信來確定衝突的事務。這些衝突可以由用戶或衝突解決規則來解決。Bucardo 是這種複製類型的一個例子。

同步多主控機複製

在同步多主控機複製中,每一個服務器能夠接受寫請求,並且在每一個事務提交之前,被修改的數據會被從原始服務器傳送給每一個其他服務器。繁重的寫活動可能導致過多的鎖定,進而導致很差的性能。事實上,寫性能通常比一個單一服務器還要糟。讀請求可以被發送給任意服務器。某些實現使用共享磁盤來減少通信負荷。同步多主控機複製主要對於讀負載最好,盡管它的大優點是任意服務器都能接受寫請求 — 沒有必要在主服務器和後備服務器之間劃分負載,並且因為數據修改被從一個服務器發送到另一個服務器,不會有非確定函數(如random())的問題。

PostgreSQL不提供這種複製類型,盡管在應用代碼或中間件中可以使用PostgreSQL的兩階段提交(PREPARE TRANSACTION和COMMIT PREPARED)來實現這種複製。

商業方案

Because 因為PostgreSQL是開源的並且很容易被擴展,一些公司已經使用PostgreSQL並且創建了帶有唯一故障轉移、複製和負載均衡能力的商業性的閉源方案。

Table 26-1總結了上述多種方案的能力。

Table 26-1. 高可用、負載均衡和複製特性矩陣


image


有一些方案不適合上述的類別:

數據分區

數據分區將表分開成數據集。每個集合隻能被一個服務器修改。例如,數據可以根據辦公室劃分,如倫敦和巴黎,每一個辦公室有一個服務器。如果查詢有必要組合倫敦和巴黎的數據,一個應用可以查詢兩個服務器,或者可以使用主/備複製來在每一台服務器上保持其他辦公室數據的一個隻讀拷貝。

多服務器並行查詢執行

上述的很多方案允許多個服務器來處理多個查詢,但是沒有一個允許一個單一查詢使用多個服務器來更快完成。這種方案允許多個服務器在一個單一查詢上並發工作。這通常通過把數據在服務器之間劃分並且讓每一個服務器執行該查詢中屬於它的部分,然後將結果返回給一個中心服務器,由它整合結果並發回給用戶。Pgpool-II具有這種能力。同樣,也可以使用PL/Proxy工具集來實現這種方案。

連續歸檔可以被用來創建一個高可用性(HA)集群配置,其中有一個或多個後備服務器隨時準備在主服務器失效時接管操作。這種能力被廣泛地稱為溫備或日誌傳送。

主服務器和後備服務器一起工作來提供這種能力,但這些服務器隻是鬆散地組織在一起。主服務器在連續歸檔模式下操作,而每一個後備服務器在連續恢複模式下操作並且持續從主服務器讀取 WAL 文件。要啟用這種能力不需要對數據庫表做任何改動,因此它相對於其他複製方案降低了管理開銷。這種配置對主服務器的性能影響也相對較低。

直接從一個數據庫服務器移動 WAL 記錄到另一台服務器通常被描述為日誌傳送。PostgreSQL通過一次一文件(WAL 段)的 WAL 記錄傳輸實現了基於文件的日誌傳送。不管 WAL 文件(16 MB)要被送到一個臨近的係統、同一站點的另一個係統或是在地球遙遠的另一端的一個係統上,它都可以在任何距離上被簡單和便宜地傳送。這種技術所需的帶寬取根據主服務器的事務率而變化。基於記錄的日誌傳送具有更細的粒度並且能夠在網絡連接上以流的方式增量傳遞 WAL 的改變(見Section 26.2.5)。

需要注意的是日誌傳送是異步的,即 WAL 記錄是在事務提交後才被傳送。正因為如此,在一個窗口期內如果主服務器發生災難性的失效則會導致數據丟失,還沒有被傳送的事務將會被丟失。基於文件的日誌傳送中這個數據丟失窗口的尺寸可以通過使用參數archive_timeout進行限製,它可以被設置成低至數秒。但是這樣低的設置大體上會增加文件傳送所需的帶寬。流複製(見Section 26.2.5)允許更小的數據丟失窗口。

這種配置的恢複性能是足夠好的,後備服務器在被激活後通常隻有片刻就可以到達完全可用。因此,這被稱為一種提供高可用性的溫備配置。從一個已歸檔的基礎備份恢複一個服務器並且前滾將需要較長時間,因此該技術隻提供了災難恢複的一種方案,而不適合於高可用性。一台後備服務器也可以被用於隻讀查詢,在這種情況下它被稱為一台熱備服務器。

2.1. 規劃

創建主服務器和後備服務器通常是明智的,因此它們可以盡可能相似,至少從數據庫服務器的角度來看是這樣。特別地,與表空間相關的路徑名將被未經修改地傳遞,因此如果該特性被使用,主、備服務器必須對表空間具有完全相同的掛載路徑。記住如果CREATE TABLESPACE在主服務器上被執行,在命令被執行前,它所需要的任何新掛載點必須在主服務器和所有後備服務器上先創建好。硬件不需要完全相同,但是經驗顯示,在應用和係統的生命期內維護兩個相同的係統比維護兩個不相似的係統更容易。在任何情況下硬件架構必須相同 — 從一個 32 位係統傳送到一個 64 位係統將不會工作。

通常,不能在兩個運行著不同主版本PostgreSQL的服務器之間傳送日誌。PostgreSQL 全球開發組的策略是不在次版本升級中改變磁盤格式,因此在主服務器和後備服務器上運行不同次版本將會成功地工作。不過,在這方麵並沒有提供正式的支持,因此我們建議讓主備服務器上運行的版本盡可能相同。當升級到一個新的次版本時,最安全的策略是先升級後備服務器 — 一個新的次版本發行更可能兼容從前一個次版本讀取 WAL 文件。

2.2. 後備服務器操作

在後備模式中,服務器持續地應用從主控服務器接收到的 WAL。後備服務器可以從一個 WAL 歸檔(restore_command)或者通過一個 TCP 連接直接從主控機(流複製)讀取 WAL。後備服務器將也嚐試恢複在後備集簇的pg_xlog目錄中找到的 WAL。那通常在一次數據庫重啟後發生,那時後備機將在重啟之前重播從主控機流過來的 WAL,但是你也可以在任何時候手動拷貝文件到pg_xlog讓它們被重播。

在啟動時,後備機通過恢複歸檔位置所有可用的 WAL 來開始,這稱為restore_command。一旦它到達那裏可用的 WAL 的末尾並且restore_command失敗,它會嚐試恢複pg_xlog目錄中可用的任何 WAL。如果那也失敗並且流複製已被配置,後備機會嚐試連接到主服務器並且從在歸檔或pg_xlog中找到的最後一個可用記錄開始流式傳送 WAL。如果那失敗並且沒有配置流複製,或者該連接後來斷開,後備機會返回到步驟 1 並且嚐試再次從歸檔裏的文件恢複。這種嚐試歸檔、pg_xlog和流複製的循環會一直重複知道服務器停止或者一個觸發器文件觸發了故障轉移。

當pg_ctl promote被運行或一個觸發器文件被找到(trigger_file),後備模式會退出並且服務器會切換到普通操作。在故障轉移之前,在歸檔或pg_xlog中立即可用的任何 WAL 將被恢複,但不會嚐試連接到主控機。

2.3. 為後備服務器準備主控機

如Section 25.3中所述,在主服務器上設置連續歸檔到一個後備服務器可訪問的歸檔目錄。即使主服務器垮掉該歸檔位置也應當是後備服務器可訪問的,即它應當位於後備服務器本身或者另一個可信賴的服務器,而不是位於主控服務器上。

如果你想要使用流複製,在主服務器上設置認證來允許來自後備服務器的複製連接。即創建一個角色並且在pg_hba.conf中提供一個或多個數據庫域被設置為replication的項。還要保證在主服務器的配置文件中max_wal_senders被設置為足夠大的值。如果要使用複製槽,請確保max_replication_slots也被設置得足夠高。

2.4. 建立一個後備服務器

要建立後備服務器,恢複從主服務器取得的基礎備份。在後備服務器的集簇數據目錄中創建一個恢複命令文件recovery.conf,並且打開standby_mode。將restore_command設置為一個從 WAL 歸檔中複製文件的簡單命令。如果你計劃為了高可用性目的建立多個後備服務器,將recovery_target_timeline設置為latest來使得該後備服務器遵循發生在故障轉移到另一個後備服務器之後發生的時間線改變。

Note: 不要把 pg_standby 或相似的工具和這裏描述的內建後備模式一起使用。如果文件不存在,restore_command應該立即返回,如果必要該服務器將再次嚐試該命令。

如果你想要使用流複製,在primary_conninfo中填入一個 libpq 連接字符串,其中包括主機名(或 IP 地址)和連接到主服務器所需的任何附加細節。如果主服務器需要一個口令用於認證,口令也應該被指定在primary_conninfo中。

如果你正在為高性能目的建立後備服務器,像主服務器一樣建立 WAL 歸檔、連接和認證,因為在故障轉移後該後備服務器將作為一個主服務器工作。

如果你正在使用一個 WAL 歸檔,可以使用archive_cleanup_command參數來移除後備服務器不再需要的文件,這樣可以最小化 WAL 歸檔的尺寸。pg_archivecleanup工具被特別設計為在典型單一後備配置下與archive_cleanup_command共同使用,見pg_archivecleanup。不過要注意,如果你正在為備份目的使用歸檔,有一些文件即使後備服務器不再需要你也需要保留它們,它們被用來從至少最後一個基礎備份恢複。

recovery.conf的一個簡單例子是:


standby_mode = 'on'
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
restore_command = 'cp /path/to/archive/%f %p'
archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r'

你可以有任意數量的後備服務器,但是如果你使用流複製,確保你在主服務器上將max_wal_senders設置得足夠高,這樣可以允許它們能同時連接。

2.5. 流複製

流複製允許一台後備服務器比使用基於文件的日誌傳送更能保持為最新的狀態。後備服務器連接到主服務器,主服務器則在 WAL 記錄產生時即將它們以流式傳送給後備服務器而不必等到 WAL 文件被填充。

默認情況下流複製是異步的(見Section 26.2.8),在這種情況下主服務器上提交一個事務與該變化在後備服務器上變得可見之間存在短暫的延遲。不過這種延遲比基於文件的日誌傳送方式中要小得多,在後備服務器的能力足以跟得上負載的前提下延遲通常低於一秒。在流複製中,不需要archive_timeout來縮減數據丟失窗口。

如果你使用的流複製沒有基於文件的連續歸檔,該服務器可能在後備機收到 WAL 段之 前回收這些舊的 WAL 段。如果發生這種情況,後備機將需要重新從一個新的基礎備 份初始化。通過設置wal_keep_segments為一個足夠高的值來確保舊 的 WAL 段不會被太早重用或者為後備機配置一個複製槽,可以避免發生這種情況。如 果設置了一個後備機可以訪問的 WAL 歸檔,就不需要這些解決方案,因為該歸檔可以 為後備機保留足夠的段,後備機總是可以使用該歸檔來追趕主控機。

如果你使用的流複製沒有基於文件的連續歸檔,你必須在主服務器上設置wal_keep_segments為一個足夠高的值來確保舊的 WAL 段不會被太早再利用,因為後備服務器可能還需要它們來追上主服務器。如果後備服務器落後太多,它需要從一個新的基礎備份進行初始化。如果你設置了一個後備服務器可訪問的 WAL 歸檔,wal_keep_segments就不是必要的,因為後備服務器總是可以使用該歸檔來追上主服務器。

要使用流複製,按Section 26.2所述建立一個基於文件的日誌傳送後備服務器。將一個基於文件日誌傳送後備服務器轉變成流複製後備服務器的步驟是把recovery.conf文件中的primary_conninfo設置指向主服務器。設置主服務器上的listen_addresses和認證選項(見pg_hba.conf),這樣後備服務器可以連接到主服務器上的偽數據庫replication。

在支持 keepalive 套接字選項的係統上,設置tcp_keepalives_idle、tcp_keepalives_interval和tcp_keepalives_count有助於主服務器迅速地注意到一個斷開的連接。

設置來自後備服務器的並發連接的最大數目。

當後備服務器被啟動並且primary_conninfo被正確設置,後備服務器將在重放完歸檔中所有可用的 WAL 文件之後連接到主服務器。如果連接被成功建立,你將在後備服務器中看到一個 walreceiver 進程,並且在主服務器中有一個相應的 walsender 進程。

2.5.1. 認證

設置好用於複製的訪問權限非常重要,這樣隻有受信的用戶可以讀取 WAL 流,因為很容易從 WAL 流中抽取出需要特權才能訪問的信息。後備服務器必須作為一個超級用戶或一個具有REPLICATION特權的賬戶向主服務器認證。我們推薦為複製創建一個專用的具有REPLICATION和LOGIN特權的用戶賬戶。雖然REPLICATION特權給出了非常高的權限,但它不允許用戶修改主係統上的任何數據,而SUPERUSER特權則可以。

複製的客戶端認證由一個在database域中指定replication的pg_hba.conf記錄控製。例如,如果後備服務器運行在主機 IP 192.168.1.100並且用於複製的賬戶名為foo,管理員可以在主服務器上向pg_hba.conf文件增加下列行:


# 允許來自 192.168.1.100 的用戶 "foo" 在提供了正確的口令時作為一個
# 複製後備機連接到主控機。
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    replication     foo             192.168.1.100/32        md5

主服務器的主機名和端口號、連接用戶名和口令在recovery.conf文件中指定。在後備服務器上還可以在~/.pgpass文件中設置口令(在database域中指定replication)。例如,如果主服務器運行在主機 IP 192.168.1.50、端口5432上,並且口令為foopass,管理員可以在後備服務器的recovery.conf文件中增加下列行:


# 後備機要連接到的主控機運行在主機 192.168.1.50 上,
# 端口號是 5432,連接所用的用戶名是 "foo",口令是 "foopass"。
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass

2.5.2. 監控

流複製的一個重要健康指標是在主服務器上產生但還沒有在後備服務器上應用的 WAL 記錄數。你可以通過比較主服務器上的當前 WAL 寫位置和後備服務器接收到的最後一個 WAL 位置來計算這個滯後量。它們分別可以用主服務器上的pg_current_xlog_location和後備服務器上的pg_last_xlog_receive_location來檢索。後備服務器的最後 WAL 接收位置也被顯示在 WAL 接收者進程的進程狀態中,即使用ps命令顯示的狀態。

你可以通過pg_stat_replication視圖檢索 WAL 發送者進程的列表。pg_current_xlog_location與sent_location域之間的巨大差異表示主服務器承受著巨大的負載,而sent_location和後備服務器上pg_last_xlog_receive_location之間的差異可能表示網絡延遲或者後備服務器正承受著巨大的負載。

2.6. 複製槽

複製槽提供了一種自動化的方法來確保主控機在所有的後備機收到 WAL 段 之前不會移除它們,並且主控機也不會移除可能導致 恢複衝突的行,即使後備機斷開也是如此。

作為複製槽的替代,也可以使用wal_keep_segments 阻止移除舊的 WAL 段,或者使用archive_command 把段保存在一個歸檔中。不過,這些方法常常會導致保留的 WAL 段比需要的 更多,而複製槽隻保留已知所需要的段。這些方法的一個優點是它們為 pg_xlog的空間需求提供了界限,但目前使用複製槽無法做到。

類似地,hot_standby和 vacuum_defer_cleanup_age保護了相關行不被 vacuum 移除,但是前者在後備機斷開期間無法提供保護,而後者則需要被設置為一個很高 的值已提供足夠的保護。複製槽克服了這些缺點。

2.6.1. 查詢和操縱複製槽

每個複製槽都有一個名字,名字可以包含小寫字母、數字和下劃線字符。

已有的複製槽和它們的狀態可以在 pg_replication_slots 視圖中看到。

槽可以通過流複製協議 或者 SQL 函數創建並且移除。

2.6.2. 配置實例

你可以這樣創建一個複製槽:


postgres=# SELECT * FROM pg_create_physical_replication_slot('node_a_slot');
  slot_name  | xlog_position
-------------+---------------
 node_a_slot |

postgres=# SELECT * FROM pg_replication_slots;
  slot_name  | slot_type | datoid | database | active | xmin | restart_lsn | confirmed_flush_lsn
-------------+-----------+--------+----------+--------+------+-------------+---------------------
 node_a_slot | physical  |        |          | f      |      |             |
(1 row)

要配置後備機使用這個槽,在後備機的recovery.conf中應該配置 primary_slot_name。這裏是一個簡單的例子:


standby_mode = 'on'
primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass'
primary_slot_name = 'node_a_slot'

2.7. 級聯複製

級聯複製特性允許一台後備服務器接收複製連接並且把 WAL 記錄流式傳送給其他後備服務器,就像一個轉發器一樣。這可以被用來減小對於主控機的直接連接數並且使得站點間的帶寬開銷最小化。

一台同時扮演著接收者和發送者角色的後備服務器被稱為一台級聯後備服務器。“更直接”(通過更少的級聯後備服務器)連接到主控機的後備服務器被稱為上遊服務器,而那些離得更遠的後備服務器被稱為下遊服務器。級聯複製並沒有對下遊服務器的數量或布置設定限製。

一台級聯後備服務器不僅僅發送從主控機接收到的 WAL 記錄,還要發送那些從歸檔中恢複的記錄。因此即使某些上遊連接中的複製連接被中斷,隻要還有新的 WAL 記錄可用,下遊的流複製都會繼續下去。

級聯複製目前是異步的。同步複製設置當前對級聯複製無影響。

不管在什麼樣的級聯布置中,熱備反饋都會向上遊傳播。

如果一台上遊後備服務器被提升為新的主控機,且下遊服務器的recovery_target_timeline被設置成'latest',下遊服務器將繼續從新的主控機得到流。

要使用級聯複製,要建立級聯後備服務器讓它能夠接受複製連接(即設置max_wal_senders和hot_standby,並且配置基於主機的認證)。你還將需要設置下遊後備服務器中的primary_conninfo指向級聯後備服務器。

2.8. 同步複製

PostgreSQL流複製默認是異步的。如果主服務器崩潰,則某些已被提交的事務可能還沒有被複製到後備服務器,這會導致數據丟失。數據的丟失量與故障轉移時的複製延遲成比例。

同步複製能夠保證一個事務的所有修改都能被傳送到一台或者多台同步後備服務器。這擴大了由一次事務提交所提供的標準持久化級別。在計算機科學理論中這種保護級別被稱為 2-safe 複製。而當synchronous_commit被設置為remote_write時,則是 group-1-safe (group-safe 和 1-safe)。

在請求同步複製時,一個寫事務的每次提交將一直等待,直到收到一個確認表明該提交在主服務器和後備服務器上都已經被寫入到磁盤上的事務日誌中。數據會被丟失的唯一可能性是主服務器和後備服務器在同一時間都崩潰。這可以提供更高級別的持久性,盡管隻有係統管理員要關係兩台服務器的放置和管理。等待確認提高了用戶對於修改不會丟失的信心,但是同時也不必要地增加了對請求事務的響應時間。最小等待時間是在主服務器和後備服務器之間的來回時間。

隻讀事務和事務回滾不需要等待後備服務器的回複。子事務提交也不需要等待後備服務器的響應,隻有頂層提交才需要等待。長時間運行的動作(如數據載入或索引構建)不會等待最後的提交消息。所有兩階段提交動作要求提交等待,包括預備和提交。

2.8.1. 基本配置

一旦流複製已經被配置,配置同步複製就隻需要一個額外的配置步驟:synchronous_standby_names必須被設置為一個非空值。synchronous_commit也必須被設置為on,但由於這是默認值,通常不需要改變。這樣的配置將導致每一次提交都等待確認消息,以保證後備服務器已經將提交記錄寫入到持久化存儲中。synchronous_commit可以由個體用戶設置,因此它可以在配置文件中配置、可以為特定用戶或數據庫配置或者由應用動態配置,這樣可以在一種每事務基礎上控製持久性保證。

在一個提交記錄已經在主服務器上被寫入到磁盤後,WAL 記錄接著被發送到後備服務器。每次一批新的 WAL 數據被寫入到磁盤後,後備服務器會發送回複消息,除非在後備服務器上wal_receiver_status_interval被設置為零。如果synchronous_commit被設置為remote_apply,當提交記錄被重放時後備服務器會發送回應消息,這會讓該事務變得可見。如果從主服務器的synchronous_standby_names優先列表中選中該後備服務器作為一個同步後備,將會根據來自該後備服務器和其他同步後備的回應消息來決定何時釋放正在等待確認提交記錄被收到的事務。這些參數允許管理員指定哪些後備服務器應該是同步後備。注意同步複製的配置主要在主控機上。命名的後備服務器必須直接連接到主控機,主控機對使用級聯複製的下遊後備服務器一無所知。

將synchronous_commit設置為remote_write將導致每次提交都等待後備服務器已經接收提交記錄並將它寫出到其自身所在的操作係統的確認,但並非等待數據都被刷出到後備服務器上的磁盤。這種設置提供了比on要弱一點的持久性保障:在一次操作係統崩潰事件中後備服務器可能丟失數據,盡管它不是一次PostgreSQL崩潰。不過,在實際中它是一種有用的設置,因為它可以減少事務的響應時間。隻有當主服務器和後備服務器都崩潰並且主服務器的數據庫同時被損壞的情況下,數據丟失才會發生。

把synchronous_commit設置為remote_apply將導致每一次提交都會等待,直到當前的同步後備服務器報告說它們已經重放了該事務,這樣就會使該事務對用戶查詢可見。在簡單的情況下,這為帶有因果一致性的負載均衡留出了餘地。

如果請求一次快速關閉,用戶將停止等待。不過,在使用異步複製時,在所有未解決的 WAL 記錄被傳輸到當前連接的後備服務器之前,服務器將不會完全關閉。

2.8.2. 多個同步後備

同步複製支持一個或者更多個同步後備服務器,事務將會等待,直到所有同步後備服務器都確認收到了它們的數據為止。事務必須等待其回複的同步後備的數量由synchronous_standby_names指定。這個參數也指定後備服務器的名稱列表,它決定了每一個後備服務器被選中為同步後備的優先級。出現在列表中越靠前優先級越高。出現在列表中靠後位置的後備服務器表示潛在的同步後備。如果任何一個當前後備服務器由於任何原因斷開連接,它將立刻被下一個具有最高優先級的後備服務器替代。

多個同步後備的synchronous_standby_names示例為:


synchronous_standby_names = '2 (s1, s2, s3)'

在這個例子中,如果有四個後備服務器s1、s2、s3和s4在運行,兩個後備服務器s1和s2將被選中為同步後備,因為它們出現在後備服務器名稱列表的前部。s3是一個潛在的同步後備,當s1或s2中的任何一個失效, 它就會取而代之。s4則是一個異步後備因為它的名字不在列表中。

2.8.3. 性能規劃

同步複製通常要求仔細地規劃和放置後備服務器來保證應用能令人滿意地工作。等待並不利用係統資源,但是事務鎖會持續保持直到傳輸被確認。其結果是,不小心使用同步複製將由於響應時間增加以及較高的爭用率而降低數據庫應用的性能。

PostgreSQL允許應用開發者通過複製來指定所要求的持久性級別。這可以為整個係統指定,不過它也能夠為特定的用戶或連接指定,甚至還可以為單個事務指定。

例如,一個應用的載荷的組成可能是這樣:10% 的改變是重要的客戶詳情,而 90% 的改變是不太重要的數據,即使它們丟失業務也比較容易容忍(例如用戶間的聊天消息)。

通過在應用級別(在主服務器上)指定的同步複製選項,我們可以為大部分重要的改變提供同步複製,並且不會拖慢整體的載荷。應用級別選項是使高性能應用享受同步複製的一種重要和實用的工具。

你應該認為網絡帶寬必須比 WAL 數據的產生率高。

2.8.4. 高可用性規劃

當synchronous_commit被設置為on、remote_apply或者remote_write時, synchronous_standby_names指定產生的事務提交要等待其回應的同步後備的數量和名稱。如果任一同步後備崩潰,這類事務提交可能無法完成。

高可用的最佳方案是確保有所要求數量的同步後備。這可以通過使用synchronous_standby_names指定多個潛在後備服務器來實現。出現在該列表前部的後備服務器將被用作同步後備。後麵的後備服務器將在當前同步後備服務器失效時取而代之。

當一台後備服務器第一次附加到主服務器時,它將處於一種還沒有正確同步的狀態。這被描述為追趕模式。一旦後備服務器和主服務器之間的遲滯第一次變成零,我們就來到了實時的流式狀態。在後備服務器被創建之後的很長一段時間內可能都是追趕模式。如果後備服務器被關閉,則追趕周期將被增加,增加量由後備服務器被關閉的時間長度決定。隻有當後備服務器到達流式狀態後,它才能成為一台同步後備。

如果在提交正在等待確認時主服務器重啟,那些正在等待的事務將在主數據庫恢複時被標記為完全提交。沒有辦法確認所有後備服務器已經收到了在主服務器崩潰時所有還未處理的 WAL 數據。某些事務可能不會在後備服務器上顯示為已提交,即使它們在主服務器上顯示為已提交。我們提供的保證是:在 WAL 數據已經被所有後備服務器安全地收到之前,應用將不會收到一個事務成功提交的顯式確認。

如果實在無法保持所要求數量的同步後備,那麼應該減少synchronous_standby_names中指定的事務提交應該等待其回應的同步後備的數量(或者禁用),並且在主服務器上重載配置文件。

如果主服務器與剩下的後備服務器是隔離的,你應當故障轉移到那些其他剩餘後備服務器中的最佳候選者上。

如果在事務等待時你需要重建一台後備服務器,確保命令 pg_start_backup() 和 pg_stop_backup() 被運行在一個synchronous_commit = off的會話中,否則那些請求將永遠等待後備服務器出現。

2.9. 在後備機上連續歸檔

當在一個後備機上使用連續歸檔時,有兩種不同的情景:WAL 歸檔在主服務器 和後備機之間共享,或者後備機有自己的 WAL 歸檔。當後備機擁有其自身的 WAL 歸檔時,將archive_mode設置為 always,後備機將在收到每個 WAL 段時調用歸檔命令, 不管它是從歸檔恢複還是使用流複製恢複。共享歸檔可以類似地處理,但是 archive_command必須測試要被歸檔的文件是否 已經存在,以及現有的文件是否有相同的內容。這要求 archive_command中有更多處理,因為它必須當心 不要覆蓋具有不同內容的已有文件,但是如果完全相同的文件被歸檔兩次時 應返回成功。並且如果兩個服務器嚐試同時歸檔同一個文件,所有這些都必須 在沒有競爭情況的前提下完成。

如果archive_mode被設置為on,歸檔器 在恢複或者後備模式中無法啟用。如果後備服務器被提升,它將在被提升後開始 歸檔,但是它將不會歸檔任何不是它自身產生的 WAL。要在歸檔中得到完整的 一係列 WAL 文件,你必須確保所有 WAL 在到達後備機之前都被歸檔。對於基於 文件的日誌傳輸來說天然就是這樣,因為後備機隻能恢複在歸檔中找到的文件, 而啟用了流複製時則不是這樣。當一台服務器不在恢複模式中時,在 on和always模式之間沒有差別。

如果主服務器失效,則後備服務器應該開始故障轉移過程。

如果後備服務器失效,則不會有故障轉移發生。如果後備服務器可以被重啟(即使晚一點),由於可重啟恢複的優勢,那麼恢複處理也能被立即重啟。如果後備服務器不能被重啟,則一個全新的後備服務器實例應該被創建。

如果主服務器失效並且後備服務器成為了新的主服務器,那麼接下來舊的主服務器重啟後,你必須有一種機製來通知舊的主服務器不再成為主服務器。有些時候這被稱為STONITH(Shoot The Other Node In The Head,關閉其他節點),這對於避免出現兩個係統都認為它們是主服務器的情況非常必要,那種情況將導致混亂並且最終導致數據丟失。

很多故障轉移係統僅使用兩個係統,主係統和後備係統,它們由某種心跳機製連接來持續驗證兩者之間的連接性和主係統的可用性。也可能會使用第三個係統(稱為目擊者服務器)來防止某些不當故障轉移的情況,但是除非非常小心地建立它並且經過了嚴格地測試,額外的複雜度可能會使該工作得不償失。

PostgreSQL並不提供在主服務器上標識失敗並且通知後備數據庫服務器所需的係統軟件。現在已有很多這樣的工具並且很好地與成功的故障轉移所需的操作係統功能整合在一起,例如 IP 地址遷移。

一旦發生到後備服務器的故障轉移,就隻有單一的一台服務器在操作。這被稱為一種退化狀態。之前的後備服務器現在是主服務器,但之前的主服務器處於關閉並且可能一直保持關閉。要回到正常的操作,一個後備服務器必須被重建,要麼在之前的主係統起來時使用它重建,要麼使用第三台(可能是全新的)服務器來重建。在大型集簇上,pg_rewind功能可以被用來加速這個過程。一旦完成,主服務器和後備服務器可以被認為是互換了角色。某些人選擇使用第三台服務器來為新的主服務器提供備份,直到新的後備服務器被重建,不過顯然這會使得係統配置和操作處理更複雜。

因此,從主服務器切換到後備服務器可以很快,但是要求一些時間來重新準備故障轉移集群。從主服務器到後備服務器的常規切換是有用的,因為它允許每個係統有常規的關閉時間來進行維護。這也可以作為一種對故障轉移機製的測試,以保證在你需要它時它真地能夠工作。我們推薦寫一些管理過程來做這些事情。

要觸發一台日誌傳送後備服務器的故障轉移,運行pg_ctl promote或者創建一個觸發器文件,其文件名和路徑由recovery.conf中的trigger_file設置指定。如果你正在規劃使用pg_ctl promote進行故障轉移,trigger_file就不是必要的。如果你正在建立隻用於從主服務器分流隻讀查詢而不是高可用性目的的報告服務器,你不需要提升它。

前一節描述的內建後備模式的一種替代方案是使用一個輪詢歸檔位置的restore_command。這是版本 8.4 及以下版本中唯一可用的選項。在這種設置中,設置standby_mode為關閉,因為你要自行實現後備操作所需的輪詢。關於這種實現的一個參考請見pg_standby模塊。

注意在這種模式中,服務器將一次應用一整個文件的 WAL,因此如果你使用後備服務器來查詢(見熱備),那麼主服務器上的一個動作和後備服務器上該動作變得可見之間會有一個延遲,該延遲對應著填滿 WAL 文件的時間。archive_timeout可以被用來縮短該延遲。還要注意你不能把流複製和這種方法組合起來使用。

在主服務器和後備服務器上都會發生的操作是通常的連續歸檔和恢複任務。兩個數據庫服務器之間唯一的接觸點是兩者共享的 WAL 文件歸檔:主服務器寫這個歸檔,後備服務器讀取這個歸檔。必須要小心地保證來自獨立主服務器的 WAL 歸檔不會混合在一起或者混淆。如果歸檔隻被後備操作需要,它不必很大。

使得兩台鬆耦合的服務器一起工作的訣竅是在後備服務器上使用的restore_command,當要求下一個 WAL 文件時,會等待它在主服務器上變得可用。restore_command在後備服務器的recovery.conf文件中指定。正常的恢複處理將從 WAL 歸檔請求一個文件,如果該文件不可用則會報告失敗。對於後備處理來說下一個 WAL 文件不可用很正常,因此後備服務器必須等待它出現。對於以.backup或.history結尾的文件沒有必要等待,並且必須返回一個非零的返回碼。一個等待的restore_command可以用一種習慣的腳本編寫,在其中輪詢下一個 WAL 文件的存在之後進行循環。也必須有某種方式來觸發故障轉移,那將打斷restore_command:打破循環並返回一個文件未找到錯誤給後備服務器。這會結束恢複並且後備服務器將接下來變成一個正常的服務器。

一個合適的restore_command的偽代碼是:


triggered = false;
while (!NextWALFileReady() && !triggered)
{
    sleep(100000L);         /* wait for ~0.1 sec */
    if (CheckForExternalTrigger())
        triggered = true;
}
if (!triggered)
        CopyWALFileForRecovery();

在pg_standby模塊中提供了一個等待的restore_command的工作例子。它也可被用作如何正確實現上述邏輯的參考。它也可以根據需要被擴展來支持指定的配置和環境。

觸發故障轉移的方法是規劃和設計中的一個重要部分。一種潛在的選項是restore_command命令。它對每一個 WAL 文件被執行一次,但是運行restore_command的進程會為每一個文件創建和死亡,因此沒有守護進程或服務器進程,並且也不能使用信號或信號句柄。因此,restore_command不適合於觸發故障轉移。可以使用一種簡單的超時功能,特別是和主服務器上已知的archive_timeout設置一起。但是,由於一個網絡問題或者繁忙的主服務器可能足以發起故障轉移,這有點容易產生錯誤。如果可以安排,一種提醒機製(例如顯式創建一個觸發器文件)是最理想的。

4.1. 實現

使用這種替代方案配置一個後備服務器的簡短過程如下所示。對於每一步的細節,可以參考之前的小節。

1.盡可能將主係統和後背係統設置成近乎一致,包括在同一發行級別上的兩個相同的PostgreSQL拷貝。

2.在後備服務器上建立從主係統到一個 WAL 歸檔目錄的連續歸檔。確保在主服務器上archive_mode、archive_command和archive_timeout被恰當地設置。

3.建立主服務器的一個基礎備份,並且把該數據載入到後備服務器。

4.在後備服務器上開始從本地 WAL 歸檔的恢複,在recovery.conf中指定一個按之前所述進行等待的restore_command。

恢複將 WAL 歸檔當作隻讀的來處理,因此一旦一個 WAL 文件已經被複製到後備係統,在它正在被後備數據庫服務器讀取時可以被同時複製到磁帶。因此,可以在為了長期災難恢複目的存儲文件的同時運行一個用於高可用性的後備服務器。

為了測試的目的,可以在一個相同的係統上運行主服務器和後備服務器。這對於服務器魯棒性並不會提供任何有意義的改進,對 HA 也一樣。

4.2. 基於記錄的日誌傳送

也可以使用這種替代方法來實現基於記錄的日誌傳送,不過這需要定製開發,並且隻有在一整個 WAL 文件被傳送之後改變才會對熱後備查詢可見。

一個外部程序可以調用pg_xlogfile_name_offset()函數來找出 WAL 的當前末端的文件名和其中準確的字節偏移。它接著可以直接訪問 WAL 文件並且將從上一個已知的 WAL 末尾到當前末尾之間的數據拷貝到後備服務器。通過這種方法,數據丟失的窗口是複製程序的輪詢周期時間,這可以為非常小,並且不會有強製部分使用的段文件被歸檔所浪費的帶寬。注意後備服務器的restore_command腳本隻能處理整個 WAL 文件,因此增量複製的數據通常不會對後備服務器可用。隻有當主服務器死掉時它才有用 — 那時最後一個部分 WAL 文件會在允許它發生之前被喂給後備服務器。這個處理的正確實現要求restore_command腳本和數據複製程序的合作。

從PostgreSQL 版本 9.0 開始,你可以使用流複製來實現事半功倍的效果。

術語熱備用來描述處於歸檔恢複或後備模式中的服務器連接到服務器並運行隻讀查詢的能力。這有助於複製目的以及以高精度恢複一個備份到一個期望的狀態。術語熱備也指服務器從恢複轉移到正常操作而用戶能繼續運行查詢並且保持其連接打開的能力。

在熱備模式中運行查詢與正常查詢操作相似,盡管如下所述存在一些用法和管理上的區別。

5.1. 用戶概覽

當hot_standby參數在一台後備服務器上被設置為真時,一旦恢複將係統帶到一個一致的狀態它將開始接受連接。所有這些連接都被限製為隻讀,甚至臨時表都不能被寫入。

後備服務器上的數據需要一些時間從主服務器到達後備服務器,因此在主服務器和後備服務器之間將有一段可以度量的延遲。近乎同時在主服務器和後備服務器上運行相同的查詢可能因此而返回不同的結果。我們說後備服務器上的數據與主服務器是最終一致的。一旦一個事務的提交記錄在後備服務器上被重播,那個事務所作的修改將對後備服務器上所有新取得的快照可見。快照可以在每個查詢或每個事務的開始時取得,這取決於當前的事務隔離級別。

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

  • 查詢訪問 - 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 SHARE 或 ROW EXCLUSIVE.
  • 計劃和資源 - PREPARE、EXECUTE、 DEALLOCATE、DISCARD
  • 插件和擴展 - LOAD

在熱備期間開始的事務將不會被分配一個事務 ID 並且不能被寫入到係統的預寫式日誌。因此,下列動作將產生錯誤消息:

  • 數據操縱語言(DML) - INSERT、 UPDATE、DELETE、COPY FROM、 TRUNCATE。注意不允許在恢複期間導致一個觸發器被執行的動作。這個限製甚至被應用到臨時表,因為不分配事務 ID 表行就不能被讀或寫,而當前不可能在一個熱備環境中分配事務 ID。

  • 數據定義語言(DDL) - CREATE、 DROP、ALTER、COMMENT。這個限製甚至被應用到臨時表,因為執行這些操作會要求更新係統目錄表。

  • SELECT ... FOR SHARE | UPDATE,因為不更新底層數據文件就無法取得行鎖。

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

  • 顯式請求一個高於ROW EXCLUSIVE MODE的模式的LOCK。

  • 默認短形式的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、UNLISTEN、NOTIFY

在正常操作中,"隻讀"事務被允許更新序列並且使用LISTEN、UNLISTEN和NOTIFY,因此熱備會話在比普通隻讀會話更緊一點的限製下操作。這些限製中的某些可能會在一個未來的發行中被放鬆。

在熱備期間,參數transaction_read_only總是為真並且不可以被改變。但是隻要不嚐試修改數據庫,熱備期間的連接工作起來更像其他數據庫連接。如果發生故障轉移或切換,該數據庫將切換到正常處理模式。當服務器改變模式時會話將保持連接。一旦熱備結束,它將可以發起讀寫事務(即使是一個在熱備期間啟動的會話)。

用戶將可以通過發出SHOW transaction_read_only來了解他們的會話是不是隻讀的。此外,一組函數(Table 9-79)允許用戶訪問關於後備服務器的信息。這些允許你編寫關心數據庫當前狀態的程序。這些可以被用來監控恢複的進度,或者允許你編寫恢複數據庫到特定狀態的複雜程序。

5.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_delay和max_standby_streaming_delay,它們定義了在 WAL 應用中的最大允許延遲。當應用任何新收到的 WAL 數據花費了超過相關延遲設置值時,衝突查詢將被取消。設立兩個參數是為了對從一個歸檔讀取 WAL 數據(即來自一個基礎備份的初始恢複或者"追趕"一個已經落後很遠的後備服務器)和通過流複製讀取 WAL數據的兩種情況指定不同的延遲值。

在一台後備服務器上這主要是為了該可用性而存在,最好把延遲參數設置得比較短,這樣服務器不會由於後備查詢導致的延遲落後主服務器太遠。但是,如果該後備服務器是位了執行長時間運行的查詢,則一個較高甚至無限的延遲值更好。但是記住一個長時間運行的查詢延遲了 WAL 記錄的應用,它可能導致後備服務器上的其他會話無法看到主服務器上最近的改變。

一旦max_standby_archive_delay或max_standby_streaming_delay指定的延遲被超越,衝突查詢將被取消。這通常僅導致一個取消錯誤,盡管在重放一個DROP DATABASE的情況下整個衝突會話都將被中斷。另外,如果衝突發生在一個被空閑事務持有的鎖上,該衝突會話會被中斷(這種行為可能在未來被改變)。

被取消的查詢可能會立即被重試(當然是在開始一個新的事務後)。因為查詢取消依賴於 WAL 記錄被重放的本質,如果一個被取消的查詢被再次執行,它可能會很好地成功完成。

記住延遲參數是從 WAL 數據被後備服務器收到後流逝的時間。因此,留給後備服務器上任何一個查詢的寬限期從不會超過延遲參數,並且如果後備服務器已經由於等待之前的查詢完成而落後或者因為過重的更新負載而無法跟上主服務器,寬限期可能會更少。

在後備查詢和 WAL 重播之間發生衝突的最常見原因是"過早清除"。正常地,PostgreSQL允許在沒有事務需要看到舊行版本時對它們進行清除,這樣可以保證根據 MVCC 規則的正確的數據可見性。不過,這個規則隻能被應用於執行在主控機上的事務。因此有可能主控機上的清除會移除對一個後備服務器事務還可見的行版本。

有經驗的用戶應當注意行版本清除和行版本凍結都可能與後備查詢衝突。即便在一個沒有被更新或被刪除行的表上運行一次手工VACUUM FREEZE也可能導致衝突。

用戶應當清楚,主服務器上被正常和重度更新的表將快速地導致後備服務器上長時間運行的查詢被取消。在這樣的情況下,max_standby_archive_delay或max_standby_streaming_delay的有限製設置可以被視作statement_timeout設置。

如果發現後備查詢取消的數量不可接受,還是有補救的可能。第一種選項是設置參數 hot_standby_feedback,它阻止VACUUM 移除最近死亡的元組並且因此清除衝突不會產生。如果你這樣做,你應當 注意這將使主服務器上的死亡元組清除被延遲,這可能會導致不希望發生 的表膨脹。不過,清除的情況不會比在主服務器上直接運行後備查詢時更糟, 並且你仍然能夠享受將執行分流到後備服務器的好處。如果後備服務器頻繁地連接和 斷開,你可能想要做些調整來處理無法提供hot_standby_feedback 反饋的時期。例如,考慮增加max_standby_archive_delay,這樣 在斷開連接的期間查詢就不會快速地被 WAL 歸檔文件中的衝突取消。你也應該考慮 增加max_standby_streaming_delay來避免重新連接後新到達的流 WAL 項導致的快速取消。

另一個選項是增加主服務器上的vacuum_defer_cleanup_age,這樣死亡行不會像平常那麼快地被清理。這將允許在後備服務器上的查詢能在被取消前有更多時間執行,並且不需要設置一個很高的max_standby_streaming_delay。但是,這種方法很難保證任何指定的執行時間窗口,因為vacuum_defer_cleanup_age是用主服務器上被執行的事務數來衡量的。

查詢取消的數量和原因可以使用後備服務器上的pg_stat_database_conflicts係統視圖查看。pg_stat_database係統視圖也包含匯總信息。

5.3. 管理員概覽

如果hot_standby在postgresql.conf中被設置為on並且存在一個recovery.conf文件,服務器將運行在熱備模式。但是,可能需要一些時間來允許熱備連接,因為在服務器完成足夠的恢複來為查詢提供一個一致的狀態之前,它將不會接受連接。在此期間,嚐試連接的客戶端將被一個錯誤消息拒絕。要確認服務器已經可連接,要麼循環地從應用嚐試連接,要麼在服務器日誌中查找這些消息:


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沒有被設置為replica或者logical的期間)產生的 WAL 時無法啟用熱備。在同時存在這些條件時,到達一個一致狀態也會被延遲:

  • 一個寫事務有超過 64 個子事務
  • 生存時間非常長的寫事務

如果你正在運行基於文件的日誌傳送(“溫備”),你可能需要等到下一個 WAL 文件到達,這可能和主服務器上的archive_timeout設置一樣長。

如果後備服務器上某些參數在主服務器上已經被改變,它們的設置將需要重新配置。對這些參數,在後備服務器上的值必須等於或者大於主服務器上的值。如果這些參數沒有被設置得足夠高,後備服務器將拒絕開始。較高的值被提供之後,服務器重新啟動再次開始恢複。這些參數是:

  • max_connections
  • max_prepared_transactions
  • max_locks_per_transaction
  • max_worker_processes

管理員為max_standby_archive_delay和max_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()將在用戶後端上工作,而不是執行恢複的 Startup 進程。pg_stat_activity不會為 Startup 進程顯示一個項,也不會把恢複事務顯示為活動。結果是在恢複期間pg_prepared_xacts總是為空。如果你希望解決不能確定的預備事務,查看主服務器上的pg_prepared_xacts並且發出命令來確定那裏的事務。

和平常一樣,pg_locks將顯示被後端持有的鎖。pg_locks也會顯示一個由 Startup 進程管理的虛擬事務,它擁有被恢複重播的事務所持有的所有AccessExclusiveLocks。注意 Startup 進程不請求鎖來做數據庫更改,並且因此對於 Startup 進程除AccessExclusiveLocks之外的鎖不顯示在pg_locks中,它們僅被假定存在。

Nagios的插件check_pgsql將可以工作,因為它檢查的簡單信息是存在的。check_postgres監控腳本也將能工作,盡管某些被報告的值可能給出不同或者混亂的結果。例如,上一次清理時間將不會被維護,因為在後備服務器上不會發生清理。在主服務器上運行的清理仍會把它們的改變發送給後備服務器。

WAL 文件控製命令在恢複期間將不會工作,如pg_start_backup、pg_switch_xlog等。

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

谘詢鎖在恢複期間工作正常,包括死鎖檢測。注意谘詢鎖從來都不會被 WAL 記錄,因此在主服務器或後備服務器上一個谘詢鎖不可能會與 WAL 重播發生衝突。也不可能會在主服務器上獲得一個谘詢鎖並且在後備服務器上開始一個相似的谘詢鎖。谘詢鎖隻與它們被取得的那個服務器相關。

基於觸發器的複製係統(如Slony、Londiste和Bucardo)將根本不會運行在後備服務器上,然而隻要改變不被發送到要被應用的後備服務器,它們將在主服務器上運行得很好。WAL 重播不是基於觸發器的,因此你不能用後備服務器接替任何需要額外數據庫寫操作或依賴觸發器使用的係統。

新的 OID 不能被分配,然而某些UUID生成器仍然能工作,隻要它們不依賴於向數據庫寫新的狀態。

當前,在隻讀事務期間不允許創建臨時表,因此在某些情況中現有的腳本將不會正確運行。這個限製可能會在稍後的發行中被放鬆。這既是一個 SQL 標準符合問題也是一個技術問題。

隻有在表空間為空時DROP TABLESPACE才能成功。某些後備服務器用戶可能正在通過他們的temp_tablespaces參數使用該表空間。如果在該表空間中有臨時文件,所有活動查詢將被取消來保證臨時

最後更新:2017-08-23 17:33:30

  上一篇:go  【專訪】KDD2018主席熊輝教授:數據挖掘與深度學習結合新趨勢
  下一篇:go  穀歌標誌性“螢火蟲”無人車退役 轉向量產汽車研發