分布式理论:CAP、BASE 与最终一致
分布式理论:CAP、BASE 与最终一致
数据存在多个节点上,就绕不开一个灵魂拷问:网络一旦出问题,你是保"数据一致",还是保"服务可用"? 这就是 CAP 定理——分布式系统的第一块理论基石,也是被误解最多的一个:很多人背得出"三选二",却说不清到底在选什么。这篇把 CAP 和它的工程化延伸 BASE、最终一致讲清楚,顺便纠几个流传很广的误会。
一、CAP 是什么
是什么。 CAP 是 2000 年 Eric Brewer 提出、后来被严格证明的一个定理。它说的是:一个分布式系统,在 C、A、P 三个特性里,没法同时百分百满足。三个字母分别是:
| 字母 | 全称 | 大白话 |
|---|---|---|
| C | Consistency 一致性 | 所有节点在同一时刻看到的是同一份数据——你刚写进去的值,从任何一个节点读出来都是最新的 |
| A | Availability 可用性 | 每个请求都能拿到响应(不是错误、不是超时),哪怕拿到的不一定是最新数据 |
| P | Partition tolerance 分区容错 | 节点之间的网络断了(消息丢失、延迟),系统仍然能继续工作 |
为什么这三个会打架。 关键在那个 P——"网络分区"。
场景。 假设你有两地两台数据库:北京一台、上海一台,平时互相同步,保证两边数据一样。某天它们之间的网线断了(网络分区发生了),这时上海来了个请求要改数据:
- 如果你坚持一致性(C):上海这台不能擅自改,因为改了没法同步给北京,两边就不一致了——于是它只能拒绝服务 / 让请求等着,这就牺牲了可用性(A)。
- 如果你坚持可用性(A):上海这台照常受理,先改了再说——但北京那台还是旧值,两边数据不一致了,这就牺牲了一致性(C)。
你看,网络一断,C 和 A 就只能保一个。这就是 CAP 的全部直觉:分区面前,一致和可用,二者不可兼得。
二、为什么"三选二"是个误解(其实是二选一)
是什么误解。 几乎所有人第一次听 CAP,都被教成"三选二"——CA、CP、AP 任选一种。这句话不算错,但极具误导性,因为它让人以为"CA"(放弃 P、同时拿到强一致和高可用)是个正经的、可选的架构。
为什么这是误解。 因为 P(分区容错)在分布式系统里不是"可选项",而是"必选项"。
道理很硬:只要你的数据真的分布在多台机器上、靠网络连着,那网络就一定会出问题——光纤会被挖断、交换机会宕、机房会失联、跨地域专线会抖。这不是"会不会"的问题,是"什么时候"的问题。墨菲定律在网络上格外灵验。
所以一个真正的分布式系统,P 必须扛住——你不可能选择"放弃 P":放弃 P 意味着"假设网络永不出错",而网络一旦出错系统就彻底乱套(数据错乱还浑然不觉),这比不可用糟糕得多。
所以 CAP 真正的取舍,从来不是"三选二",而是在 P 已经锁定的前提下,C 和 A 之间的"二选一"。
这就是为什么业界把系统粗分成两类:CP(保一致、舍可用)和 AP(保可用、舍一致)。至于 "CA" 系统——严格说只存在于"网络永不分区"的幻想里,现实中那是单机数据库(就一个节点,压根没有分区一说),不在分布式 CAP 的讨论范围内。把单机系统叫"CA"去和 CP/AP 并列,正是混乱的源头。
一句话点破:CAP 不是让你在三个里挑两个,而是网络出问题那一刻,你只能在"宁可不可用"和"宁可不一致"之间选一个站队。
三、实际场景:CP 系统 vs AP 系统
光说理论太虚,看两类真实系统怎么站队。
CP:宁可不可用,也不给错数据。 适合那些"数据错了比服务停了更可怕"的场景。最典型的就是配置中心、分布式协调、选主这类基础设施:
- 业界方案:ZooKeeper、etcd(后者是 Kubernetes 存集群状态的大脑)。它们底层用 Raft / ZAB 这类一致性协议,要求多数节点(quorum)达成一致才算写成功。
- 场景。 比如一个集群要选一个"主节点",这种事绝不能选出俩主(脑裂),否则两个主各自发号施令,数据直接乱套。所以当网络分区、节点凑不齐多数派时,ZooKeeper/etcd 宁可这部分暂时不可写、报错让你重试,也绝不在不确定的状态下硬给你一个可能错误的答案。这就是 CP:用短暂的不可用,换绝对的正确。
AP:宁可读到旧数据,也要能用。 适合那些"服务挂了用户立刻就跑、但数据晚一会儿对上没人在意"的场景:
- 业界方案:Cassandra、DynamoDB 这类高可用数据库,以及早期微服务里的注册中心 Eureka。
- 场景。 拿服务注册中心举例:它存的是"哪些服务实例还活着"。网络分区时,Eureka 的设计哲学是——就算各分区之间信息没完全同步,我也要继续提供查询,让调用方至少能拿到一份(可能略旧的)服务列表,总比整个注册中心罢工、所有服务互相找不到要强。这就是 AP:用一点点数据陈旧,换服务始终在线。
注意一个常被忽略的点:CP/AP 不是给整个公司贴标签,而是给"每一类数据 / 每一个组件"做选择。 同一家电商,钱和库存那条链路往往要 CP(强一致),而商品浏览、点赞数那条链路完全可以 AP(最终一致)。架构师的功夫,就在于给不同的数据选不同的取舍,而不是全站一刀切。
四、BASE 与最终一致
是什么。 如果说 CAP 是那道"非黑即白"的选择题,BASE 就是 AP 阵营在工程上的落地方法论。BASE 是三个词的缩写,和化学里"碱(base)对酸(ACID)"是个故意的双关:
- BA(Basically Available,基本可用):出问题时牺牲一部分功能 / 性能,保住核心可用——比如大促时降级掉非核心功能、给你排个队,但下单主流程不能崩。
- S(Soft state,软状态):允许系统存在中间状态——数据可以"正在同步中",不要求时时刻刻都是终态。
- E(Eventually consistent,最终一致):不追求时时刻刻一致,但保证经过一段时间后,数据最终会对上。
ACID 是"要么全成、要么全不成"的刚性事务(数据库篇的强一致世界);BASE 是"先让系统转起来、数据慢慢对齐"的柔性思路。前者刚,后者柔。
为什么互联网大多选最终一致。 一句话:强一致的代价太大了。 这一点在《缓存》篇里已经亲身领教过——想让缓存和数据库时时刻刻强一致,代价极高,几乎要把缓存的性能优势抵消掉,所以通行做法是接受最终一致:允许极短时间不一致,靠"删缓存 + 下次回填 + TTL 兜底"让数据最终对上。
把这个结论放大到整个分布式系统就是 BASE:对绝大多数互联网业务,用户对"快"和"一直能用"的感知,远强于对"那一两秒的数据偏差"的感知。为了消灭那一两秒的偏差,去付出"系统变慢、变脆弱"的代价,不划算。
场景。 哪些数据天生适合最终一致:
- 订单状态。 你下单后,"支付成功 → 通知库存 → 通知物流 → 给你发短信"这一长串,不可能在一个瞬间同时完成。中间状态(支付成功但物流还没创建)是允许存在的软状态,只要最终所有环节都对上就行。
- 点赞数、阅读量、粉丝数。 你点了个赞,数字晚一两秒、甚至几分钟才精确,没有任何人会因此投诉。这类计数天生为最终一致而生。
- 评论、动态的扩散。 你发条动态,粉丝不是同一秒全看到,而是慢慢"扩散"开——这也是最终一致。
一句话点破:互联网的默认姿势是 BASE / 最终一致,只有少数"碰钱碰命"的链路才退回 ACID / 强一致。
五、注意事项
- CAP 是个简化模型,别当圣经。 它只描述了"网络分区那一刻"的取舍,却没说"网络正常时该怎么办"。补这个缺口的是 PACELC:Partition 时在 A 和 C 之间选(这就是 CAP);Else(没分区时)还要在 Latency(延迟)和 Consistency(一致性)之间选。也就是说,哪怕网络好好的,你想要更强的一致性,往往也得付出更高的延迟(等多个副本都确认)。真实选型考虑的是 PACELC 这个更完整的图景,CAP 只是它的"分区子集"。
- "最终一致"不是甩锅的借口。 "反正最终会一致"——这话很容易被用来掩盖 bug。最终一致必须配兜底机制:对账(定时核对两边数据、把不一致的修回来)、重试 + 幂等(《消息队列》篇讲过,下游晚一会儿处理完,但不能处理错、不能重复扣)、TTL 过期(《缓存》篇的兜底)。没有兜底的"最终一致",本质就是"永远不一致"。
- 强一致场景(尤其是钱),老实付代价。 转账、扣库存、扣余额这类——少一分、多一分都是事故,不能拿最终一致糊弄。该上分布式事务(2PC、TCC、Saga,留给《分布式事务》篇)就上,该直接读主库不走缓存就别图快。这类场景的代价(慢、复杂)是必须付的成本,不是可以省的开销。
- 别迷信"AP 就是高可用"。 AP 系统也可能因为别的原因(机器全挂、容量打满)而不可用;CAP 里的 A 只保证"非分区故障时请求有响应",不等于工程意义上的"四个九高可用"——后者是更大的话题(《高可用》篇)。
- 绝大多数系统其实是"分链路混合"的。 一个真实系统里,核心交易链路走 CP/强一致,外围展示链路走 AP/最终一致,很常见。别被"我们公司是 CP 还是 AP"这种问法带偏——正确的问法是"这条链路、这份数据,该选哪个"。
六、一张表:CP vs AP
| 维度 | CP(保一致) | AP(保可用) |
|---|---|---|
| 网络分区时,选择 | 宁可拒绝服务,也不给错数据 | 宁可返回旧数据,也要有响应 |
| 牺牲的是 | 可用性(A)——部分请求会失败 / 等待 | 一致性(C)——可能读到陈旧数据 |
| 底层常用技术 | Raft / ZAB / Paxos(多数派达成一致) | 多副本异步复制、最终一致同步 |
| 典型系统 | ZooKeeper、etcd、Consul(可调) | Cassandra、DynamoDB、Eureka |
| 适合的数据 / 场景 | 配置、选主、分布式锁、钱 / 库存 | 点赞数、订单状态流转、服务列表、动态扩散 |
| 一句话 | 用短暂不可用,换绝对正确 | 用一点数据陈旧,换服务永远在线 |
名词解释
- CAP 定理:分布式系统在一致性(C)、可用性(A)、分区容错(P)三者中无法同时完全满足;由于 P 几乎必选,真正的取舍是 C 与 A 二选一。
- 一致性(Consistency,C):所有节点同一时刻看到同一份数据,读到的总是最新写入的值。注意它和数据库 ACID 里的 C(约束一致)含义不同,这里指多副本之间的一致。
- 可用性(Availability,A):每个请求都能在合理时间内得到响应(非错误、非超时),哪怕数据未必最新。
- 分区容错(Partition tolerance,P):节点间网络断开 / 丢包时,系统仍能继续工作。分布式系统中网络故障不可避免,故 P 通常是必选项。
- CP 系统:网络分区时优先保证一致性、牺牲可用性的系统,如 ZooKeeper、etcd。
- AP 系统:网络分区时优先保证可用性、牺牲强一致的系统,如 Cassandra、Eureka。
- BASE:Basically Available(基本可用)+ Soft state(软状态)+ Eventually consistent(最终一致),是 AP 思路的工程化方法论,与刚性的 ACID 相对。
- 最终一致(Eventual Consistency):不要求时时刻刻一致,但保证经过一段时间后数据最终对齐;互联网业务的默认选择。
- 强一致(Strong Consistency):任何时刻、从任何节点读到的都是最新值;代价高,多用于"碰钱碰命"的链路。
- PACELC:CAP 的补充模型——分区(P)时在 A 与 C 间权衡;否则(E,Else)在延迟(L)与一致性(C)间权衡。强调"即使网络正常,一致性也要用延迟来换"。
- 脑裂(Split-brain):网络分区导致集群里出现多个"主节点"、各自为政、数据错乱的故障;CP 系统靠多数派机制避免它。
- 多数派 / Quorum:分布式一致性协议中,操作需获得超过半数节点确认才生效,用以保证一致与避免脑裂。
- Raft / Paxos / ZAB:分布式一致性协议,让多个节点对"同一份数据 / 同一个决定"达成一致,是 CP 系统的底层基石。
- 对账:定时核对多处数据是否一致、并把不一致修正回来的兜底手段;最终一致系统的标配。
本文属《研发都要懂的事》·服务端架构设计系列。这是该系列的分布式理论篇——CAP / BASE / 最终一致是后面所有分布式话题的"地基"。顺着这块地基,接下来会进入更具体的工程难题:分布式事务(钱怎么在多个服务间不出错地流转)、高可用(怎么把可用性做到几个九)。理论是用来"看懂取舍"的,真正的硬仗在每一条具体链路上。
评论(0)
登录后参与评论。
还没有评论,来抢沙发吧。

