允許多個節點同時接受寫入。它解決了單主在跨機房、離線、協作場景下的可用性與延遲問題,但代價是把整個架構最棘手的東西放到檯面上:寫衝突。
🧠 Intuition
TIP
把它想成「每個機房都有自己的 leader,各自先收寫入、事後再彼此對帳」。可用性與寫延遲變好了,但兩邊同時改同一筆資料時,沒有單一權威能裁決誰對——衝突解決因此成為主角而非例外。
- 每個 leader 各自接受寫入,並把變更非同步複製給其他 leader。
- 三個經典場景:多資料中心(每機房一個 leader,跨機房延遲與斷線可容忍)、離線客戶端(手機/筆電本地就是一個 leader,連上線再同步)、協作編輯(每個使用者的編輯先在本地生效)。
- 本質是「每個寫入點都是 leader-follower 中的 leader」,但加上 leader 之間的雙向(多向)複製。
⚖️ Tradeoffs
為什麼要用多主 — 它買到什麼
- 跨機房寫延遲低:寫入命中本地機房 leader,不必繞到遠端單一 leader。
- 跨機房容錯:某機房或機房間網路斷掉時,本地仍可寫入,恢復後再追平。
- 離線可用:離線裝置照樣寫本地,回到線上才同步——本質就是「機房=裝置」的多主。
- 代價:放棄了單主那種「天然無衝突」的簡單性。
寫衝突的來源 — 多主的原罪
- 兩個 leader 各自接受了對同一筆資料的寫入,複製到對方時才發現分歧。
- 衝突偵測在非同步複製下天生是「事後」的——寫入當下回了成功,衝突要到複製碰頭才暴露。
- 同步偵測衝突可行,但那等於放棄多主的核心優勢(各自獨立接受寫入),不如直接用單主。
- 最佳策略往往是從源頭避免衝突:把某筆資料的所有寫入路由到固定同一個 leader(例如按 user id 分流),讓衝突根本不會發生。
衝突解決的四種手段
- LWW(末寫者勝,Last Write Wins):給每筆寫入掛時間戳,取最大者。簡單但會默默丟資料,且依賴時鐘——時鐘偏移會讓「贏家」變得隨機。
- 版本向量 (Version vectors):追蹤因果關係,能正確分辨「真衝突」與「一方涵蓋另一方」,再把真衝突交給應用處理。
- 自訂合併 (Application-level merge):寫入時記錄衝突、讓應用在讀取時或回呼中合併(例如保留雙方值讓使用者選)。
- CRDT:設計成數學上可自動合併的資料結構(如計數器、集合、文字),合併滿足交換律/結合律/冪等,收斂無需協調——協作編輯的主流方向。
複製拓撲 (Topology) — leader 之間怎麼連
- 全連 (All-to-all):每個 leader 把寫入發給所有其他 leader。最穩健、無單點,但訊息可能亂序到達,需要版本向量保因果。
- 環狀 / 星狀 (Circular / Star):訊息沿固定路徑轉發,連線數少;但單一節點掛掉就阻斷複製路徑,容錯差。
- 拓撲越密越能容錯,但越要正視「訊息亂序/因果」問題——全連是大多數生產系統的選擇。
🔑 Takeaways
- 多主買到的是跨機房低延遲寫入、容錯與離線可用;賣掉的是單主「天然無衝突」的簡單性。
- 衝突在非同步複製下是事後才暴露的——能避免就避免(同一筆資料路由到固定 leader 是最有效的招)。
- 解決手段由弱到強:LWW(會丟資料)→ 版本向量(保因果)→ 自訂合併 → CRDT(可自動收斂),依「資料可不可丟」選擇。
- 拓撲選全連最穩健,但要用版本向量處理訊息亂序;環狀/星狀省連線卻有單點阻斷風險。
2026-07-01
- Q: 既然 LWW 那麼簡單,為什麼大家都說它危險?
- A: LWW 的「贏家」靠時間戳決定,而分散式系統的時鐘不可信——機器間時鐘偏移會讓兩個幾乎同時的寫入,由毫秒級的時鐘誤差來裁決勝負,輸的那筆直接被靜默丟棄、毫無痕跡。在「資料可丟」的場景(如快取、可重算的派生資料)LWW 沒問題;但在「不能丟寫入」的場景,它會造成難以察覺的資料遺失。要正確處理就得升級到版本向量(保留真衝突交給應用)或 CRDT(設計成本就能自動正確合併)。