一致性不是開關,是光譜
弱 ←————————————————————————————→ 強
Eventual < Causal < Read-your-writes < Linearizability
越強的一致性 → 越高的延遲 / 越低的可用性(CAP 的代價)。
各層的定義
Eventual Consistency(最終一致性)
最弱保證:「如果停止更新,最終所有副本會收斂到同一個值。」
有用的地方:DNS propagation(全球 DNS 最終都會有新紀錄),CDN cache(最終都會過期更新),社群媒體的 Like 計數(晚個幾秒沒差)。
沒有保證的地方:無法保證讀到自己剛寫的資料,無法保證讀的順序,無法保證多個副本讀到同一個值。
Read-Your-Writes(讀自己的寫)
保證:你寫入的資料,下一個你發出的讀一定能讀到。
沒有保證:別人可能還讀不到你剛寫的資料。
實作方式:
- 寫入後的讀操作指定發給主庫(Master Read After Write)
- 或:帶著 session token,router 根據 token 路由到已有最新資料的副本
- 或:Client 本地快取剛寫的資料,在複製完成前用本地值回應讀
使用場景:用戶發文後立刻看到自己的貼文(但不保證別人的 timeline 馬上有)。
Monotonic Reads(單調讀取)
保證:你不會在讀到新資料之後,下一次讀到更舊的資料。
場景:避免「看到了 v3 版本的資料,下一個請求讀到 v1」這種時光倒流現象。
實作:把同一個用戶的讀請求路由到同一個副本。
Causal Consistency(因果一致性)
保證:有因果關係的操作按因果順序被看到。
A 寫入 "Hello"
B 讀到 "Hello",然後寫入 "World" (B 的寫入因果上依賴 A 的寫入)
C 讀到 "World",一定也能讀到 "Hello"
(不能是:C 看到 "World" 但沒看到 "Hello")
沒有因果關係的操作可以以任何順序被看到。
實作:Vector Clock(上一篇)追蹤因果關係;寫入時帶 vector clock,讀取時確認因果依賴已滿足才回應。
Linearizability / Strong Consistency(線性一致性)
最強保證:所有操作看起來像是瞬間在某個時間點發生,所有節點對操作的順序達成一致。
效果等同於只有一個節點——任何讀都看到最新的寫。
代價:寫入需要等多數派確認(Quorum Write),讀取也可能需要和多數派確認(Quorum Read),延遲顯著增加。
不同系統提供的 Consistency Level
| 系統 | 提供的層級 |
|---|---|
| DynamoDB | Eventual(預設)或 Strong(額外費用) |
| Cassandra | Tunable(ONE / QUORUM / ALL),ONE = Eventual,ALL ≈ Strong |
| MongoDB | Eventual(Secondary Read)或 Strong(Primary Read + majority writeConcern) |
| Google Spanner | Linearizability(TrueTime API 保證) |
| etcd / ZooKeeper | Linearizability |
選型指引
用 Eventual Consistency 的條件:
- 資料短暫不一致不影響業務(Like 計數、瀏覽量、DNS)
- 高可用和低延遲比正確性更重要
用 Read-Your-Writes 的條件:
- 用戶需要能看到自己剛做的操作(發文、更新 profile)
- 別人不需要即時看到,但自己需要
用 Causal Consistency 的條件:
- 有依賴關係的操作需要被按順序看到(留言回覆、訊息 thread)
用 Linearizability 的條件:
- 分散式鎖(必須確保只有一個人拿到)
- 金融 transaction(不能讀到中間狀態)
- 配置更新(所有節點要同時看到新設定)