具代表性的面試主題

後端面試:如何設計 PostgreSQL 邏輯複製故障切換?

後端困難
Offer.cc 編輯團隊發佈 更新

題幹

你維護一個從 PostgreSQL 生產庫讀取變更的 CDC 消費者。主庫計畫切換或意外故障時,如何讓新主庫繼續提供同一邏輯複製槽,同時避免重複、遺失、WAL 堆積和錯誤宣稱零資料遺失?

題幹與適用場景

你維護一個從 PostgreSQL 生產庫讀取變更的 CDC 消費者。主庫計畫切換或意外故障時,如何讓新主庫繼續提供同一邏輯複製槽,同時避免重複、遺失、WAL 堆積和錯誤宣稱零資料遺失?請說明 PostgreSQL 18 的 failover slot、同步確認、消費者重連、監控和回退方案。

PostgreSQL 文件說明,邏輯複製槽可以同步到實體 standby,但槽同步是非同步的;提升 standby 前必須確認所需槽已同步並處於 failover_ready。這是一道後端可靠性題,重點是把資料庫能力、消費者語義和故障切換編排連成可驗證流程。

面試官考察點

  • 能否區分實體 WAL 複製、邏輯複製槽和 CDC 消費者確認位點。
  • 能否解釋 failover 槽、槽同步和 synchronized_standby_slots 的作用邊界。
  • 能否在 planned switchover 與突發故障下分別處理遺失窗口和重複事件。
  • 是否知道槽同步非同步,不能僅憑 standby 在線就宣稱可切換。
  • 是否設計 WAL 保留、槽延遲、消費者重連和告警指標。
  • 是否明確 PostgreSQL 之外的協調器、DNS、連線字串和下游冪等責任。

回答前需要澄清的問題

  1. 消費者是另一個 PostgreSQL subscriber,還是 Debezium 等非 PostgreSQL CDC 客戶端?兩者的就緒檢查不同。
  2. 目標是計畫內零或近零遺失,還是故障後的可接受恢復點?這決定是否允許未同步 WAL 被捨棄。
  3. 下游是否按 LSN、交易 ID 或業務鍵去重?重複事件能否安全重播?
  4. 主備是否同一叢集、同一 PostgreSQL 大版本和相同輸出外掛?
  5. 誰負責提升、切換連線位址、凍結舊主庫並防止雙主寫入?

30 秒回答框架

我會先定義 CDC 的恢復點和重複容忍度,再把槽同步、提升、消費者重連和下游冪等拆成狀態機。為需要跨故障繼續消費的邏輯槽啟用 failover 能力,並在計畫切換前檢查 standby 上每個槽的存在、同步狀態和 failover_ready。切換時先隔離舊主庫,再更新連線探索;消費者從新主庫繼續讀取,可能重複的交易由 LSN 或業務冪等處理。持續監控槽延遲、WAL 保留、消費延遲和重連結果,不把非同步槽同步當成零遺失保證。

分步驟深入解答

1. 先定義資料語義

把 RPO、RTO 和重複策略寫進方案。計畫內切換可等待所有目標槽同步,突發故障只能從新主庫已持久化且已同步的位置繼續。下游至少要保存最後確認的 LSN 或等價位點;業務寫入應使用冪等鍵,避免一次交易重播造成重複副作用。

2. 設定可故障切換的邏輯槽

PostgreSQL 18 支援建立可同步到 standby 的邏輯槽,建立槽或訂閱時需要啟用 failover 選項。對需要繼續消費的每個槽建立清單,避免只設定主流槽而遺漏表同步槽或其他消費者。

sql
-- 僅示意:建立邏輯槽時允許其同步到 standby
SELECT *
FROM pg_create_logical_replication_slot('cdc_orders', 'pgoutput', false, true);

實際語法和參數應以執行版本文件為準;面試回答應說明這段 SQL 只是示意,不能繞過權限、外掛和訂閱設定檢查。

3. 讓實體複製把槽狀態帶到 standby

設定實體 standby 接收 WAL,並按文件要求設定 synchronized_standby_slots,使涉及邏輯故障切換的操作等待指定實體槽確認 WAL 已到達。槽複製仍可能延遲,因此必須在切換前讀取 standby 的 pg_replication_slots,確認目標槽存在、synced 狀態有效且 failover_ready 為真。

4. 計畫內切換流程

先暫停或排空 CDC 消費者,記錄最後確認位點;再停止舊主庫寫入並等待實體複製追平。對每個消費者核對主庫和 standby 的槽清單,確認所有所需槽已就緒後才提升 standby。切換連線探索並解除消費者暫停,消費者使用同一邏輯槽繼續讀取;如果從位點邊界重播,依靠 LSN 或下游冪等消除重複。

5. 突發故障與雙主保護

突發故障無法等待槽同步完成,應先阻止舊主庫恢復後繼續寫入,再根據新主庫已確認 WAL 評估恢復點。協調器必須保證同一時間只有一個可寫主庫;DNS、VIP 或服務探索切換後,消費者仍需驗證伺服器身分和槽存在。未同步的邏輯槽不能被描述為完整繼承,必要時應進入人工恢復或重建訂閱流程。

6. 監控 WAL 堆積和消費者進度

監控每個槽的 restart_lsnconfirmed_flush_lsn、槽延遲、WAL 保留量、消費者重連次數和端到端延遲。槽長期無人消費會阻止 WAL 回收,最終耗盡磁碟;消費者恢復時應有逾時、限速和人工接管閾值。把「槽存在」與「消費者真正處理到目標 LSN」分開告警。

7. 回退、重建與驗收

若 standby 不滿足就緒條件,不執行自動提升,保留舊主庫或進入明確的降級流程。故障演練要涵蓋計畫切換、主庫突然斷電、消費者斷線、槽同步延遲和舊主庫誤恢復。驗收記錄切換時最後提交、首個新主庫事件、重複數、缺口數、RTO 和磁碟峰值;任一缺口都應追溯到具體 LSN。

高品質示範回答

我會把方案建模為「槽同步就緒、隔離舊主、提升 standby、切換探索、消費者續讀、下游確認」六個狀態。對 CDC 所需邏輯槽啟用 failover,並用 synchronized_standby_slots 和 standby 上的 pg_replication_slots 檢查,確認每個槽存在且 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 或業務冪等鍵去重,並在演練中記錄重播數量、最終業務計數和順序約束,比較切換前後的可審計位點。

公開來源

同類題目