因果一致 (Causal Consistency)

★★★★Reviewed

FromDDIA

📚 From the Books

因果一致 (Causal Consistency)Consistency

只保住「有因果關係的操作」彼此的順序,放掉「無關操作」的全域即時排序。它是在網路分區下仍能維持高可用的最強一致性模型——拿到了線性一致八成的好處,卻不必付全域協調的代價。

🧠 Intuition

TIP

因果一致 = 「只排該排的順序」。如果 B 操作能「看見」A(讀到 A 寫的值、或在同一個 client 接續 A 之後),那所有人都必須先看到 A 再看到 B;但兩件並發 (concurrent)、互不知情的操作,誰先誰後可以各副本自行決定。

⚖️ Tradeoffs

因果序 vs 全序 — 為什麼放掉全序這麼關鍵
  • 線性一致要把所有操作排成單一全序,這需要跨副本的全域協調(共識),分區時就掛。
  • 因果一致只維護 happens-before 偏序,並發操作不需協調 → 各副本可在本地先生效、事後傳播。
  • 代價:失去即時性,存在「不同 client 看到並發事件順序不同」的窗口——但絕不會看到果在因前
為何是分區下最強的一致性
  • 有個理論結果:因果一致是「在容忍分區、保持可用」前提下能達到的最強一致性模型 (Mahajan et al.)。
  • 直覺:再強一點就需要對並發操作定全序,而定全序 = 需要全域同步 = 分區時要嘛阻塞要嘛拒絕服務。
  • 所以實務上「想要強保證又不想犧牲可用性」的系統(如 COPS、MongoDB causal sessions)都停在這一層。
怎麼實作 — 版本向量 / Lamport timestamp
  • 版本向量 (version vectors):每個副本一個計數器,能精確判斷兩事件是 happens-before 還是並發——並發就保留為衝突,交給應用解決。
  • Lamport timestamp:(counter, nodeId) 給出與因果相容的全序,但無法區分「真並發」與「真有序」,只保證若 A→B 則 ts(A)<ts(B)。
  • 取捨:版本向量資訊完整但大小隨副本數成長;Lamport timestamp 輕巧但會把並發強行定序、丟失並發資訊。
一致前綴讀 (Consistent Prefix Reads)
  • 因果一致蘊含一致前綴讀:讀者看到的,一定是某個「合法前綴」的寫入序列,不會出現「看到答案卻看不到對應問題」。
  • 違反案例多發生在分片系統:不同 partition 各自非同步複製,跨片的因果(A 在片 1、B 在片 2)若無協調就可能倒置。
  • 修法:用因果序追蹤(時間戳/依賴標記)讓跨片讀也尊重 happens-before。

🔑 Takeaways

✍️ My Notes

2026-07-01

  • Q: 因果一致跟「最終一致」差在哪?兩個不是都允許讀到舊值嗎?
  • A: 都允許讀到舊值,但最終一致不保證順序——它只承諾「停止寫入後副本終會收斂」,過程中你可能看到果在因前(先看到留言、後看到原貼)。因果一致額外保證 happens-before 不被違反:有因果關係的操作對所有人都同序。換句話說,因果一致是「帶順序保證的最終一致」,是最終一致之上、線性一致之下的中間檔。

📖 Further Reading

🔗 Dive Deeper