一致性不是開關,是光譜

弱 ←————————————————————————————→ 強
 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

系統提供的層級
DynamoDBEventual(預設)或 Strong(額外費用)
CassandraTunable(ONE / QUORUM / ALL),ONE = Eventual,ALL ≈ Strong
MongoDBEventual(Secondary Read)或 Strong(Primary Read + majority writeConcern)
Google SpannerLinearizability(TrueTime API 保證)
etcd / ZooKeeperLinearizability

選型指引

用 Eventual Consistency 的條件

  • 資料短暫不一致不影響業務(Like 計數、瀏覽量、DNS)
  • 高可用和低延遲比正確性更重要

用 Read-Your-Writes 的條件

  • 用戶需要能看到自己剛做的操作(發文、更新 profile)
  • 別人不需要即時看到,但自己需要

用 Causal Consistency 的條件

  • 有依賴關係的操作需要被按順序看到(留言回覆、訊息 thread)

用 Linearizability 的條件

  • 分散式鎖(必須確保只有一個人拿到)
  • 金融 transaction(不能讀到中間狀態)
  • 配置更新(所有節點要同時看到新設定)