最終一致性的真正代價
你有一份資料,要放在 N 個複本(replica)上做容錯。某天一台機器壞了、或者你想加裝一台新的。
系統需要搬多少資料才能恢復平衡?傳統做法(用 hash(key) mod N 直接分片)的答案是「幾乎全部」。
把複本擺在一個圓環上、用「順時針找下一個」的規則指派擁有權,可以把搬遷量壓到大約 K / N。
下面是為什麼。
關鍵想法:把節點擺在環上,不要擺在線上
把每個 複本節點 和每筆資料都用同一個雜湊函數, 投影到同一個 環(雜湊輸出空間,頭尾相接)。 一筆資料屬於哪個節點?從它的位置順時針走,第一個遇到的節點就是它的主人。 當某個節點離開時,只有它那一段 弧 上的資料需要轉手—— 交給順時針方向的下一個鄰居——其他資料的歸屬完全不動。
不同顏色的弧代表不同節點的擁有權範圍。移除一個節點時,它的弧合併到順時針鄰居; 其他弧上的每一個點(資料)顏色不變——這就是整個機制要保護的不變量。
對照組:hash mod N 為什麼不行
| hash mod N | 環狀指派 | |
|---|---|---|
| N→N+1 時資料搬遷比例 | 約 (N−1)/N 全部要動 | 約 1/(N+1) |
| 負載均勻度 | 建構上即均勻 | 會偏斜——用 虛擬節點 修正 |
| 查詢成本 | O(1) | O(log N)(環上做二分搜尋) |
| 實際使用者 | 陣列分片、簡單負載均衡器 | Dynamo、Cassandra、Memcached client、Envoy |
什麼場景會遇到
只要你正在把狀態分散到一個大小會變動的池子裡:快取叢集、分片佇列、物件儲存、有 sticky session 的請求路由。 實際生產環境跑的並不是「一個物理節點對應環上一個點」這種樸素版,而是 虛擬節點 版本—— 每個物理節點佔據環上很多小弧,而不是一條大弧。這樣負載更平均,加減機器時的擾動也更小。
理解這個機制的真正價值,不是記住一個演算法名字,而是看穿一個更普遍的設計權衡: 當系統規模會動態變化,你寧可付出 O(log N) 的查詢成本,也不願意付出 O(N) 的重平衡成本。 這個取捨在很多地方反覆出現——一致性 hash 只是它最乾淨的範例之一。