後端面試:如何設計 PostgreSQL 邏輯複製故障切換?
題干與適用場景
你維護一個從 PostgreSQL 生產庫讀取變更的 CDC 消費者。主庫計畫切換或意外故障時,如何讓新主庫繼續提供同一邏輯複製槽,同時避免重複、遺失、WAL 堆積和錯誤宣稱零資料遺失?請說明 PostgreSQL 18 的 failover slot、同步確認、消費者重連、監控和回退方案。
PostgreSQL 文件說明,邏輯複製槽可以同步到實體 standby,但槽同步是非同步的;提升 standby 前必須確認所需槽已同步並處於 failover_ready。這是一道後端可靠性題,重點是把資料庫能力、消費者語義和故障切換編排連成可驗證流程。
面試官考察點
- 能否區分實體 WAL 複製、邏輯複製槽和 CDC 消費者確認位點。
- 能否解釋
failover槽、槽同步和synchronizedstandbyslots的作用邊界。 - 能否在 planned switchover 與突發故障下分別處理遺失窗口和重複事件。
- 是否知道槽同步非同步,不能僅憑 standby 在線就宣稱可切換。
- 是否設計 WAL 保留、槽延遲、消費者重連和告警指標。
- 是否明確 PostgreSQL 之外的協調器、DNS、連線字串和下游冪等責任。
回答前需要澄清的問題
- 消費者是另一個 PostgreSQL subscriber,還是 Debezium 等非 PostgreSQL CDC 客戶端?兩者的就緒檢查不同。
- 目標是計畫內零或近零遺失,還是故障後的可接受恢復點?這決定是否允許未同步 WAL 被捨棄。
- 下游是否按 LSN、交易 ID 或業務鍵去重?重複事件能否安全重播?
- 主備是否同一叢集、同一 PostgreSQL 大版本和相同輸出外掛?
- 誰負責提升、切換連線位址、凍結舊主庫並防止雙主寫入?
30 秒回答框架
我會先定義 CDC 的恢復點和重複容忍度,再把槽同步、提升、消費者重連和下游冪等拆成狀態機。為需要跨故障繼續消費的邏輯槽啟用 failover 能力,並在計畫切換前檢查 standby 上每個槽的存在、同步狀態和 failover_ready。切換時先隔離舊主庫,再更新連線探索;消費者從新主庫繼續讀取,可能重複的交易由 LSN 或業務冪等處理。持續監控槽延遲、WAL 保留、消費延遲和重連結果,不把非同步槽同步當成零遺失保證。
分步驟深入解答
1. 先定義資料語義
把 RPO、RTO 和重複策略寫進方案。計畫內切換可等待所有目標槽同步,突發故障只能從新主庫已持久化且已同步的位置繼續。下游至少要保存最後確認的 LSN 或等價位點;業務寫入應使用冪等鍵,避免一次交易重播造成重複副作用。
2. 設定可故障切換的邏輯槽
PostgreSQL 18 支援建立可同步到 standby 的邏輯槽,建立槽或訂閱時需要啟用 failover 選項。對需要繼續消費的每個槽建立清單,避免只設定主流槽而遺漏表同步槽或其他消費者。
-- 僅示意:建立邏輯槽時允許其同步到 standby
SELECT *
FROM pg_create_logical_replication_slot('cdc_orders', 'pgoutput', false, true);實際語法和參數應以執行版本文件為準;面試回答應說明這段 SQL 只是示意,不能繞過權限、外掛和訂閱設定檢查。
3. 讓實體複製把槽狀態帶到 standby
設定實體 standby 接收 WAL,並按文件要求設定 synchronizedstandbyslots,使涉及邏輯故障切換的操作等待指定實體槽確認 WAL 已到達。槽複製仍可能延遲,因此必須在切換前讀取 standby 的 pgreplicationslots,確認目標槽存在、synced 狀態有效且 failover_ready 為真。
4. 計畫內切換流程
先暫停或排空 CDC 消費者,記錄最後確認位點;再停止舊主庫寫入並等待實體複製追平。對每個消費者核對主庫和 standby 的槽清單,確認所有所需槽已就緒後才提升 standby。切換連線探索並解除消費者暫停,消費者使用同一邏輯槽繼續讀取;如果從位點邊界重播,依靠 LSN 或下游冪等消除重複。
5. 突發故障與雙主保護
突發故障無法等待槽同步完成,應先阻止舊主庫恢復後繼續寫入,再根據新主庫已確認 WAL 評估恢復點。協調器必須保證同一時間只有一個可寫主庫;DNS、VIP 或服務探索切換後,消費者仍需驗證伺服器身分和槽存在。未同步的邏輯槽不能被描述為完整繼承,必要時應進入人工恢復或重建訂閱流程。
6. 監控 WAL 堆積和消費者進度
監控每個槽的 restartlsn、confirmedflush_lsn、槽延遲、WAL 保留量、消費者重連次數和端到端延遲。槽長期無人消費會阻止 WAL 回收,最終耗盡磁碟;消費者恢復時應有逾時、限速和人工接管閾值。把「槽存在」與「消費者真正處理到目標 LSN」分開告警。
7. 回退、重建與驗收
若 standby 不滿足就緒條件,不執行自動提升,保留舊主庫或進入明確的降級流程。故障演練要涵蓋計畫切換、主庫突然斷電、消費者斷線、槽同步延遲和舊主庫誤恢復。驗收記錄切換時最後提交、首個新主庫事件、重複數、缺口數、RTO 和磁碟峰值;任一缺口都應追溯到具體 LSN。
高品質示範回答
我會把方案建模為「槽同步就緒、隔離舊主、提升 standby、切換探索、消費者續讀、下游確認」六個狀態。對 CDC 所需邏輯槽啟用 failover,並用 synchronizedstandbyslots 和 standby 上的 pgreplicationslots 檢查,確認每個槽存在且 failover_ready 為真;槽同步是非同步的,所以 standby 在線不等於可安全提升。
計畫切換時暫停消費者、凍結舊主庫寫入並等待實體複製和槽同步完成,再提升 standby。突發故障則先防止舊主庫復活成雙主,接受文件和監控所能證明的恢復點,不承諾零遺失。消費者保存 LSN,切換後從新主庫重連;重複交易由 LSN 檢查和下游冪等處理。最後持續監控槽延遲、WAL 堆積、消費延遲、重連和缺口,並透過斷電與延遲演練驗證 RPO、RTO。
常見錯誤
- 看到 standby 在線就直接提升 → 槽同步可能仍未完成 → 檢查每個槽的存在、同步狀態和
failover_ready。 - 把邏輯槽當成消費者已處理的位點 → 槽狀態與下游確認不同 → 分別保存和監控 LSN、消費位點。
- 承諾故障切換零資料遺失 → 突發故障可能有未同步 WAL → 說明 planned 與 unplanned 的不同 RPO。
- 只切換 DNS 不隔離舊主庫 → 可能出現雙主寫入 → 先 fencing,再提升和切換探索。
- 忽略槽導致 WAL 堆積 → 無人消費會阻止 WAL 回收 → 設定延遲、磁碟和最長保留告警。
- 只測試資料庫提升 → 消費者、輸出外掛和下游也可能失敗 → 做端到端切換與重播演練。
追問及應對
failover_ready 為真就代表不會遺失事件嗎?
不代表。它說明相關邏輯槽已同步到目標 standby 並可在提升後繼續使用;是否遺失還取決於故障發生時的實體複製確認、消費者確認位點和下游處理語義。
計畫切換為什麼還要暫停消費者?
暫停可以固定最後確認位點,避免切換期間消費者同時連線舊主和新主,便於判斷重複與缺口。也可以使用更複雜的協調器,但必須證明不會產生雙寫或亂序。
非 PostgreSQL CDC 客戶端如何驗證?
它不能直接複用訂閱查詢,應維護自己的槽清單和健康檢查,確認槽在 standby 存在且已同步,再以客戶端協定驗證新主連線和位點連續性。
如果槽不同步但業務必須恢復怎麼辦?
先宣布實際 RPO,選擇保守恢復點;可重建槽、重新快照或從業務備份補償。不能把未驗證的複製狀態包裝成無損恢復。
如何防止舊主庫重新上線?
使用雲平台或叢集協調器的 fencing、隔離網路和寫入憑證輪換;僅修改 DNS 不足以阻止舊主繼續接受寫入。
怎樣證明下游沒有重複副作用?
讓下游按交易 LSN、事件 ID 或業務冪等鍵去重,並在演練中記錄重播數量、最終業務計數和順序約束,比較切換前後的可審計位點。