postgresql , 流複制 , stream replication , wal receiver , 喚醒時機 , 狀态機
前段時間有位網友提的問題,
當postgresql資料庫的standby節點crash後再啟動,發現standby節點的wal receiver程序很久才啟動并開始從主節點接收wal。
這段時間是在等待standby節點恢複pg_xlog目錄中已有的xlog日志。

這是為什麼呢?
postgresql在crash後,需要從最近的一個檢查點開始恢複,因為在每次檢查點(如果是standby可能稱為restart ckpt)開始後,發生變更的塊都會記錄對應的full page到wal日志檔案中,從檢查點恢複,可以保證不會因為作業系統的partial write 導緻資料塊的不一緻。
對于standby節點也是一樣的,啟動時,從最近的一次檢查點開始恢複,它可以選擇ckpt或者restart ckpt開始恢複,選最近的即可。
在standby上也可以建立檢查點(稱為restart ckpt),後面會講到。
這裡隻解釋了一個問題,就是資料庫crash後從哪裡開始恢複,還有一個問題沒搞明白?
為什麼wal receiver程序很久才啟動并開始從主節點接收wal?
得先講講資料庫有幾種恢複來源:
3種恢複來源,
然後我們再分析一下,它如何選擇恢複來源的?
1. 首先會找pg_xlog目錄有沒有需要的日志(包括tli),如果有,會從日志目錄中直接讀取pg_xlog進行恢複,直到pg_xlog目錄中沒有需要的檔案,則會将source置為下一個可用的source。
2. 當source=stream時,并且wal receiver程序沒有啟動時,發信号啟動它。
3. 如果stream失敗,會重新掃描tli,然後等待wal_retrieve_retry_interval這個時間間隔,
循環往複。
代碼詳見
整理一下啟動wal receiver的流程如下
相關代碼
src/include/storage/pmsignal.h
src/backend/postmaster/postmaster.c
src/backend/replication/walreceiverfuncs.c
src/backend/access/transam/xlog.c
standby建立的檢查點稱為restart ckpt, 目的是防止正常的停止standby後,還要從正常的檢查點位置開始恢複。
建立了restart ckpt後,standby重新開機時,從restart ckpt開始恢複即可。
src/backend/postmaster/checkpointer.c
了解了原理,我們來想想現在的機制會存在什麼問題。
1. 如果備庫恢複速度較主慢,接收到的1024mb日志,隻恢複到了512mb,然後wal receiver程序突然挂了。
此時,standby會将恢複source切到pg_xlog或resotre_command,由于pg_xlog裡面還有512mb沒有恢複,那麼會等這512mb恢複完後,才會發生source的切換,再次喚醒wal receiver。
2. standby crash,沒有産生shutdown restart ckpt.
crash後重新開機,需要從最近的restart ckpt或者ckpt進行恢複,如果這之間有許多pg_xlog,那麼也需要恢複一段時間,進而wal receiver的喚醒時間也會被拖長。
帶來的問題就是:主庫和備庫的sender wal位點差異會受到一定的影響。如果正好此時主庫挂了,缺失的日志可能會比較多。
1. 備庫在接收xlog時,記錄xlog接收到的位點資訊,進而xlog不需要等apply位點來擷取狀态。
2. 并行接收,不要等apply請求喚醒wal receiver,使用獨立的程序receive。
但是可能引入另一個問題,比如備庫就是apply較慢,導緻沒有apply的xlog堆積在備庫的pg_xlog目錄。
3. 并行恢複,由于postgresql是實體的備庫,效率已經很高了,通常不需要并行恢複,首先要考慮的是備庫的iops能力。