Appearance
CAP 定理:网络分区时的一致性与可用性
原文发布:2026-03-29 · 最后核对:2026-10-12
修订说明:原文把虚构支付故障写成真实事件,还错误地称三节点集群失联一个节点就失去多数派。本文删除该事件和未经证实的数字,改用明确的假设场景。
先看一个三节点例子
假设一个复制了同一份账户数据的集群有 A、B、C 三个节点,写入需要至少两个节点确认。某次网络故障后,A 与 B 仍能通信,C 与它们隔离。
- A 和 B 构成 2/3 多数派,可以继续确认写入。
- C 无法独自确认写入。若要求读取最新已确认值,C 也不能在无法联系多数派时自行承诺读到最新数据。
- 若故障变成 A、B、C 彼此无法通信,就没有任何一侧拥有多数派,写入会停下。
因此,“三节点失联一个节点,整个集群就无法写入”是错的。实际行为还取决于客户端连接到哪一侧、读请求是否要求线性一致,以及具体系统的故障处理。etcd 的故障模式说明也明确区分多数派与少数派。
CAP 中的三个词
Gilbert 与 Lynch 对 CAP 的正式讨论针对可读写的复制对象:
| 符号 | 在该模型里的含义 |
|---|---|
| C(一致性) | 操作看起来像按单一顺序原子完成;这里接近线性一致性,不等于数据库事务里的“约束一致性”。 |
| A(可用性) | 非故障节点收到的请求最终能按服务规格得到响应;简单返回错误不能把原本失败的读写算作成功。 |
| P(分区容错) | 节点之间的消息可能丢失或被无限期延迟,系统仍要面对这种通信故障。 |
在可能发生通信故障的网络里,系统无法同时保证上述强一致读写和所有请求都得到有效响应。这是一个保证层面的不可能性结论,并非说每次网络变慢都会立即让整个服务停机。定义与证明见 Gilbert、Lynch 的原始论文和后续回顾。
为什么“三选二”会误导
“三选二”把网络分区说成产品团队可以关闭的功能。现实中,只要服务依赖跨节点通信,就需要定义消息中断时的行为。分区期间,系统可以让某些操作等待或失败以保住强一致性,也可以允许某些操作继续、承受暂时的数据分歧;不同接口还可以选择不同策略。
例如商品详情页在短时间内显示上一次价格,可能还能接受;余额扣减若允许双方在隔离期间各自确认,可能破坏不能透支的业务约束。是否允许旧读、是否拒绝新写、是否在恢复后对账,都要按操作和不变量设计,不能给整个产品贴一个“CP”或“AP”标签就结束讨论。
分区之外还要考虑什么
CAP 没有给出响应时间、吞吐量、成本或故障恢复时间。它说的是极端网络条件下哪些保证不能同时成立。工程设计还要回答:
- 哪些数据必须满足线性一致,哪些读可以接受有界陈旧?
- 多数派所在区域发生变化时,客户端怎样发现并重试?
- 少数派是否提供只读服务?若提供,页面怎样标注数据可能过期?
- 恢复通信后如何处理冲突、重复请求与未完成交易?
- 对支付类操作,数据库约束、幂等键和对账机制分别承担什么职责?
这些问题比“选择 CP 还是 AP”更接近真实的系统设计。若服务需要不同级别的保证,应把每个接口的可见性、失败响应和恢复流程写成可测试的契约。