本页目录

CAP 与一致性模型

CAP 不是"三选二"——网络分区不可避免,发生时必须在一致性和可用性之间抉择。从 linearizability 到 eventual,一致性不是开关而是一条光谱,每种选择都有对应的工程代价和适用场景。

CAP 定理 (Brewer 2000)

CAP 不是"一致性、可用性、分区容忍性三选二"。Eric Brewer 本人的原意是:⁠网络分区是不可避免的,当它发生时,你必须在一致性和可用性之间做选择。

这三个术语在分布式语境中的精确定义:

  • Consistency (一致性): 等价于 Linearizability——所有操作表现得像是按某个全局顺序原子执行的。读操作必定返回最近一次写的结果。不是"最终一致",不是"read-your-writes"——是最强的形式化定义。
  • Availability (可用性): 每个非故障节点都能对请求做出非错误的响应。注意——不是"返回最新数据",只是"必须返回一个 response 而非 error/timeout"。返回旧数据也算 available。
  • Partition Tolerance (分区容忍性): 系统在任意网络分区下仍能继续运作。网络分区 = 部分节点之间的消息丢失/延迟超过阈值。

所以更准确的表述是:⁠当网络正常时,可以选择 CA(强一致 + 高可用)。当网络分区发生时,必须放弃 C 或 A——你不可能同时保证"所有节点看到新数据"和"所有节点都能立刻响应"。

为什么不能同时有 CA(当 P 发生时)

最简单的例子:两个节点 A 和 B,网络断开形成分区。Client 写 x=1 到 A。现在另一个 client 从 B 读 x。

  • 如果 B 立刻返回旧值(保证可用性)→ 不一致(放弃 C)
  • 如果 B 拒绝响应直到从 A 同步(保证一致性)→ 不可用(放弃 A)

没有 magic——这是物理定律。A 和 B 之间的消息延迟在最坏情况下可以是无穷大。

PACELC 扩展

CAP 只讨论了"分区发生时"。PACELC 加了另一半:⁠当网络正常(没有分区),必须在 Latency 和 Consistency 之间权衡⁠——因为正常的消息往返也需要时间。强一致性的操作(如 linearizable read)就算网络完美,也要等多数节点确认 → 更高的延迟。

一致性光谱(从强到弱)

Linearizability (强一致性, 原子一致性)

定义:每个操作看起来都是在它被调用和返回之间的某个瞬间原子执行的。所有操作有一个全局顺序⁠,且这个顺序与实时时间一致——如果操作 A 在操作 B 开始之前就返回了,则 A 在全局顺序中必定在 B 之前。

Linearizability:操作顺序必须与实时时间一致

Client1 write(x=1) write(x=2) t1 t2

Client2 read(x) read(x)

夹在 t1、t2 之间 → 必须返回 1 落在 t2 之后 → 必须返回 2 全局顺序必须与实时时间一致——这正是 linearizability 比 sequential consistency 更强的地方。

满足 linearizability 的系统:etcd (ReadIndex + Raft), ZooKeeper (sync 后读 leader), Google Spanner (TrueTime)。

Sequential Consistency (顺序一致性)

比 linearizability 弱一点:所有操作有一个全局顺序,但这个顺序不需要尊重实时时间⁠——只要每个客户端自己的操作顺序被保留。

也就是:client 1 的 write(1) 在 write(2) 之前,client 2 的 read 可以在 write(2) 之前看到 2(即使 write(2) 在 read 之后才被调用)——只要 client 1 的操作顺序是 1→2。这在实践中很常见:异步复制到 follower,client 2 可能先看到新的。

Causal Consistency (因果一致性)

比顺序一致性更弱:只保证有因果关系的操作在所有节点看到相同顺序,并发操作可以任意顺序。

因果关系定义 (Lamport's happens-before):如果操作 A 发生在 B 之前(同一客户端),或 A 的结果被 B 观察到并通过某种方式影响了 B(如 B 读了 A 写的值),则 A→B。A→B 意味着所有节点必须先看到 A 再看到 B。

没有因果关系的并发操作:顺序无所谓,最终会通过 CRDT 或版本向量解决。

实现:向量时钟(Dynamo/Riak),Lamport 时间戳(MongoDB 副本集的 lastWriteDate)。

Eventual Consistency (最终一致性)

最弱的保证:如果不再有新的写入,最终所有副本会收敛到相同的值。不保证"多快"、"中间会看到什么"。

常见于 DNS、CDN、多 leader 异步复制(MySQL replication lag)。

各模型的形式化差异

模型全局顺序尊重实时时间因果保证并发处理
LinearizabilityN/A (所有操作有序)
SequentialN/A
Causal部分 (因果链)N/A版本向量/CRDT
EventualN/ALWW/CRDT

实践中的代价

强一致性 = 延迟更高 + 可用性更低:

  • 延迟⁠: linearizable read 需要等多数节点响应(Raft ReadIndex + 心跳确认 leader 身份)→ 1-2 RTT
  • 可用性⁠: 分区时 linearizable 系统必须拒绝写入(etcd 在 minority partition → readonly)
  • 扩展性⁠: 所有写必须经过 leader(Raft/Paxos 的 log replication)→ 写吞吐受限于单节点

为什么很多系统选择最终一致性:Amazon Dynamo 的设计哲学——购物车服务如果因为一致性而不可用,损失的是直接的销售额。DNS 作为全球最大的分布式数据库,选择了几分钟到几小时的一致性延迟来换取极致的可用性。

参考

  • 论文⁠: "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services" (Gilbert & Lynch, 2002 — CAP 的形式化证明)
  • 论文⁠: "Consistency Tradeoffs in Modern Distributed Database System Design" (PACELC, 2012)
  • 论文⁠: "Linearizability: A Correctness Condition for Concurrent Objects" (Herlihy & Wing, 1990)

关键词: CAP, PACELC, linearizability, sequential consistency, causal consistency, eventual consistency, happens-before