一個節點可能在任意一行程式碼中間被凍結十幾秒——GC、VM 遷移、換頁都會這麼幹——而它自己毫不知情。醒來後它以為只過了一瞬,世界卻已經改選了新 leader。這一頁講的是:既然節點連「自己還是不是 leader」都無法確定,正確性要靠什麼守住。
🧠 Intuition
TIP
節點無法確知任何事,只能從(不)收到的訊息去猜。所以正確性不能建立在「我以為我還持有鎖」之上,而要建立在兩件外部事實上:多數決 (quorum) 決定誰還活著、fencing token 讓過期者的寫入被資源端當場擋下。
- 一個持有租約 (lease) 的 leader,可能在「檢查租約還有效」與「真正處理請求」之間被暫停 15 秒;醒來時租約早已過期、別人已接管,它卻渾然不知照樣寫下去。
- 長暫停的來源多到防不勝防:GC stop-the-world(有時數分鐘)、VM 即時遷移、筆電合蓋休眠、換頁 (paging/swap)、同步磁碟 I/O、甚至一個
SIGSTOP。 - 結論:節點必須假設自己隨時可能被凍結很久,期間別人繼續跑、甚至已宣告它死亡。
⚖️ Tradeoffs
真相由多數決定 (the truth is defined by the majority)
- 半斷線的節點就算發現「我的訊息沒被回應」,也無力改變其他人的判決;經歷長 GC 的節點恢復後根本不知道已過了一分鐘。
- 所以節點不能信任自己對局勢的判斷。許多演算法靠 quorum 投票:決策要最低數量的節點同意。若 quorum 判你死了,即使你自認活著也得服從。
- 最常用絕對多數(過半),因為一個系統裡只會有一個多數,不會冒出兩個多數做出互相矛盾的決策。
Fencing token:擋下過期者的寫入
- 真實 bug(HBase 曾發生):Client 1 拿到租約後長 GC、租約過期;Client 2 拿到同一租約並寫入;Client 1 恢復後仍以為租約有效也寫入同一檔 → 資料損毀。
- 解法 fencing:鎖服務每次發鎖都附一個單調遞增的 fencing token;每個寫入都要帶上當前 token。
- 運作:Client 1 帶 token 33、暫停、租約過期;Client 2 帶 token 34 寫入成功;Client 1 恢復後帶 token 33 來寫,資源端發現「已處理過更高的 34」→ 拒絕 33。ZooKeeper 的
zxid/cversion可當 token。 - 關鍵紀律:檢查必須在資源端做、主動拒絕過時 token;不能只靠客戶端自律——服務不該假設客戶端總是乖的。
拜占庭故障:從「不可靠但誠實」到「會說謊」
- fencing 能擋無意出錯的節點(不知租約過期),但擋不住刻意偽造 token 的節點。
- 本書的預設假設是節點不可靠但誠實 (unreliable but honest):可能慢、可能狀態過時,但只要回應就照協議說實話。若節點會說謊(傳任意錯誤/偽造回應),就是拜占庭故障 (Byzantine fault),此時達成共識即拜占庭將軍問題。
- 值得付出拜占庭容錯成本的場景很少:航太(輻射翻轉記憶體)、多方互不信任的系統(如區塊鏈)。一般資料中心節點同屬一個組織、可合理信任,拜占庭協議成本過高。
- 但**弱形式的「說謊」**仍要防:封包被硬體損壞(TCP/UDP checksum 偶爾漏抓)、輸入要驗證、NTP 查多台伺服器排除離群值。
系統模型:拿什麼假設去證明正確性
- 時序模型三種:同步(延遲/暫停/時鐘誤差都有上界——不現實但好推理)、部分同步(多數時候像同步、偶爾超界——最貼近現實)、非同步(不做任何時序假設、甚至沒有時鐘——最受限)。
- 節點故障模型三種:crash-stop(掛了就永不回來)、crash-recovery(可恢復,持久儲存在崩潰間保留、記憶體遺失)、Byzantine(任意行為)。真實系統通常用 部分同步 + crash-recovery 這組。
- 安全性 (safety) vs 活性 (liveness):safety 是「壞事永不發生」(如 fencing token 的唯一與遞增),一旦違反可指出確切時刻且無法挽回;liveness 是「好事終會發生」(如請求終會有回應)。分散式演算法通常要求 safety 在任何情況都成立,而 liveness 只在附加條件下保證(如多數存活且網路最終恢復)。
🔑 Takeaways
- 節點可能在任意位置被凍結很久(GC、VM、換頁)而不自知——所以「我以為我還是 leader」永遠不可信。
- 正確性靠兩個外部支點:quorum 多數決決定誰活著(一個系統只能有一個多數)、fencing token 讓過期寫入在資源端被擋。
- 預設假設是「不可靠但誠實」;只有航太、區塊鏈這類場景才值得付拜占庭容錯的代價。
- 用系統模型(時序 × 故障)界定假設,並區分 safety(永遠要守) 與 liveness(附條件保證)——這是判斷一個分散式演算法對不對的框架。
No notes yet — jot your takeaways or Q&A here.