本页目录
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 的系统: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)。
各模型的形式化差异
| 模型 | 全局顺序 | 尊重实时时间 | 因果保证 | 并发处理 |
|---|---|---|---|---|
| Linearizability | 是 | 是 | 是 | N/A (所有操作有序) |
| Sequential | 是 | 否 | 是 | N/A |
| Causal | 部分 (因果链) | N/A | 是 | 版本向量/CRDT |
| Eventual | 否 | N/A | 否 | LWW/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