CAP理论

前言

CAP 定理由 Eric Brewer 于 1999 年提出,它描述了分布式系统中最基本的权衡关系,是分布式系统设计与决策的重要理论基础。

本文从定义、权衡、与 ACID/BASE 的关系、以及实际应用等多个角度系统地阐述 CAP 理论的核心思想及其在工程实践中的启示。


CAP 定理

定义

CAP 定理指出,分布式系统中的三个性质无法同时完全满足,其中 P(分区容错性)是必须的,因此在实际设计中只能在 **C(一致性)**与 **A(可用性)**之间进行权衡。

三个性质的具体定义如下:

一致性(Consistency,C)

定义:任何进程对数据的读操作都会获得最近一次写操作后的结果,即所有节点在同一时刻看到相同的数据副本。

实质:系统表现得如同一个单机系统,存在一个全局的、实时更新的单一数据视图。

例子:账户余额的转账操作——转账完成后,任何时刻在任何节点查询该账户余额,都会返回最新值。

可用性(Availability,A)

定义:系统在合理的时间内(即不超过预定的超时阈值),对每个非故障的请求都会返回有效的响应。

关键点

  • “合理的时间”是指响应不能任意延迟。
  • 响应必须是有效的(non-null),而不是错误或超时。
  • 仅适用于非故障节点,故障节点可被排除。

例子:电商平台在高流量期间能快速应答用户请求,即使底层数据还未完全同步。

分区容错性(Partition Tolerance,P)

定义:当网络发生分区(节点间通信中断),系统的两个或多个部分无法相互通信时,系统仍需继续运行。

本质:在现实网络环境中,网络分区是必然出现的故障,系统必须能够在此类故障下保持一定的功能。

例子:数据中心间网络中断,两个数据中心形成孤立的分布式集群,仍需各自提供服务。

核心权衡

关键结论:当网络分区发生时(P 必须满足),系统无法同时保证 C 和 A,必须做出以下选择之一:

$$
\text{当} \quad P = \text{必须} \quad \Rightarrow \quad \text{要么选择 } C \text{ 要么选择 } A
$$

直观理解

想象一个由两个节点(X 和 Y)组成的系统,网络分区将其分为两个孤立分区:

场景 操作 结果
分区发生后,选择 A(继续服务) X 分区继续接受写请求,Y 分区也处理写请求 X 和 Y 上的数据出现分歧,违反 C
分区发生后,选择 C(保持一致) 中止一侧分区的写操作,等待恢复 该侧对用户请求无法响应,违反 A
分区不发生(无网络中断) 双向通信正常 C 和 A 都可同时满足,但需要放弃 P(不现实)

CAP 理论的常见误解

  1. “CAP 是 3 选 2”:这是不准确的。P 是强制的,只能在 C 和 A 中选择。
  2. “分区很少发生,可以忽略”:虽然分区频率相对较低,但一旦发生,必须有应对策略。
  3. “只要更新代码/配置就能同时实现 C、A、P”:这违反了数学上的不可能性。

超时与分布式决策

1999 年 Brewer 的经典论述忽略了一个重要因素:超时与分区紧密相关

在实际系统中,进程无法确定对方已崩溃还是网络延迟过长。超时阈值的设置本质上就是分区检测机制

  • 当某个操作超时时,系统必须做出根本决策:取消操作(选择 C)还是继续执行(选择 A)
  • 使用共识算法(如 Paxos)进行通信重试只是延迟了这个决策,最终仍必须在 C 和 A 中选择。
  • Paxos 实际上倾向于保证一致性,因此在分区时会无限期等待,实质上是选择了 CP

ACID 与 CAP 的关系

ACID:单机数据库的设计原则

ACID 是数据库事务的四个关键特性,保证了单机环境中数据操作的可靠性

四个特性

特性 中文 定义
A 原子性 事务的所有操作要么全部成功,要么全部失败回滚。通常通过 Undo Log 实现。
C 一致性 事务执行前后,数据库状态从一个一致态转换到另一个一致态,所有约束(主键、外键、检查约束等)必须被满足。
I 隔离性 并发事务之间相互独立,一个事务的中间状态对其他事务不可见。有四个隔离级别:读未提交、读已提交、可重复读、串行化。
D 持久性 事务提交后,其修改对数据库的影响是永久的,即使系统崩溃也不会丢失。通过 WAL(Write-Ahead Log)或 Redo Log 实现。

ACID vs CAP 中的 Consistency

方面 ACID C CAP C
含义 事务保持数据库的所有规则和约束 单副本一致性(Single-copy consistency)
作用域 单机或副本集内(前提是同步复制) 整个分布式系统的全局视图
范围 更严格,要求维持所有不变式(invariants) 更弱,只要求数据视图一致
在分区时 无法跨分区维持,分区恢复后需要重建 ACID 一致性 同样无法在分区两侧同时维持

ACID 的隔离级别与分布式系统的关联

  • 读未提交(Read Uncommitted):允许读取未提交的数据,最弱。
  • 读已提交(Read Committed):解决脏读,只能读取已提交的数据。
  • 可重复读(Repeatable Read):解决不可重复读,同一事务中的多次读返回一致结果。
  • 串行化(Serialization):最强级别,事务完全串行执行。

可串行化(serializability)通常需要跨节点通信,在分布式系统的分区情况下无法维持,因此分布式系统往往采用更弱的一致性模型。


BASE 理论

定义与核心思想

BASE 理论是为分布式系统而生的设计哲学,其核心思想是:放弃强一致性,接受数据的短暂不一致,从而确保系统的高可用性和分区容错性

BASE 是一个缩写,代表三个概念:

基本可用(Basically Available)

定义:系统在遭遇故障时(如节点宕机、网络分区),仍保证核心功能可用,允许损失部分功能或体验。

具体体现

  • 功能降级:关闭非核心功能,保留核心交易功能。
  • 响应时间变长:系统可能需要更多时间来处理请求。
  • 部分用户隔离:某些用户可能被转向至备用服务。

例子:双十一期间,电商平台关闭”商品推荐”、”用户评价”等非核心功能,但保证”浏览商品”、”加购物车”、”下单”、”支付”等核心功能的可用。

软状态(Soft State)

定义:系统允许数据存在中间状态,这些状态可能在没有新输入的情况下随时间推移而变化。

本质:数据副本间允许暂时不一致,没有强制的实时同步要求。

例子

  • 微博发布:用户发布微博后,该内容不会立刻出现在所有粉丝的信息流中,而是通过异步复制逐步传播。
  • 购物车:用户在手机端加入购物车,此时服务器缓存已更新,但云存储可能还未同步。

vs 硬状态(Hard State):硬状态要求数据在任何时刻都完全一致,这在分布式系统中难以实现且性能低下。

最终一致性(Eventual Consistency)

定义:系统保证,如果一段时间内没有新的数据更新,那么所有的数据访问最终都会获得最新写入的值。

关键点

  • 不保证立即一致性,但保证最终会一致
  • 一致收敛的时间取决于网络延迟、副本数量、同步策略等因素。

数学表述
$$
\exists , T: \quad \forall , t > T, \quad \text{所有副本的值相同}
$$

例子:DNS 系统、社交媒体的”点赞数”等计数器。

BASE vs ACID

方面 ACID BASE
设计目标 保证数据的强一致性和可靠性 保证系统的高可用和可伸缩性
一致性 强一致性,立即生效 最终一致性,允许中间不一致
可用性 为保证一致性可能降低可用性 优先保证可用性
应用场景 金融、订单等强一致性需求 社交、推荐等容忍不一致的应用
实现难度 相对简单(单机数据库) 较复杂(需处理分布式不一致)

不变式与分支管理(Invariants & Branch Reconciliation)

不变式的定义与重要性

不变式(Invariant):系统中必须始终满足的条件或约束。例如:

  • 账户余额 ≥ 0
  • 库存数 ≥ 0
  • 订单总金额 = 商品价格之和

在强一致性系统中(CP),不变式由锁和事务机制自动保护。但在选择 AP(BASE)的系统中,分布式两侧可能各自更新状态,导致:

  1. 不变式临时被破坏(如超卖、余额为负)。
  2. 分区恢复后需要主动修复

分支与合并

网络分区发生时,系统状态从单一状态 $S$ 演变为两个分支 $S_1$(分区一侧)和 $S_2$(分区另一侧):

1
2
3
4
5
6
7
    S
/ \
S1 S2 <- 分区期间发生分支
| |
op1 op2 <- 可能违反不变式
| |
S1' S2'

分区恢复后,系统需要合并这两个分支,关键问题是:

  1. 哪些操作是分区容忍的?(可以在孤立分区中执行)
  2. 哪些操作在分区时应该被禁止?(保留给分区恢复时)
  3. 如何检测和修复违反的不变式?
  4. 需要哪些补偿操作?(compensating transactions)

解决方案:可交换操作与 CRDT

1. 限制操作(Conservative Approach)

在分区期间,禁止某些可能破坏不变式的操作,从而降低可用性,但保证恢复的正确性。

例子:在分区期间禁止库存扣减,只允许查询。

2. CRDT(Conflict-free Replicated Data Type)

CRDT 是一类特殊的数据结构,设计使得在分区期间的所有操作都可交换且自动收敛

核心原理

  • 确保所有操作是交换律满足的(commutative),即操作顺序不影响最终结果。
  • 或将数据表示在格结构(lattice)上,使所有操作是单调递增的

例子

  • Set CRDT(集合):添加操作是交换的,删除操作也是,并集收敛。
  • Counter CRDT(计数器):每个副本维护独立计数,最终值是所有计数的和。
  • Amazon 购物车:删除商品与添加商品的交换——最终结果是两侧操作的并集。

CRDT 的局限

某些操作无法表示为交换的或单调的。例如,对集合的同时添加和删除

1
2
3
4
分区两侧分别执行:
1:add(item) + delete(item) = {}
2delete(item) = {}
结果收敛到 {},但可能不符合业务逻辑

解决方案是维护两个集合(add-set 和 remove-set),最终集合为其差集。


一致性的范围(Consistency Scope)

单分区内的强一致性

在主分区(primary partition)内部,系统可以通过同步复制、共识协议等手段同时实现强一致性和高可用性。

例子

  • Paxos 在同一数据中心内的多个节点间可以保证强一致性。
  • Raft 集群在多数派确认前,一致性同样得到保证。

跨分区的一致性权衡

在广域分布式系统中(如多数据中心),设计者通常采取:

  • 主分区模型:主数据中心采用强一致性(通过 Paxos 或 Raft),次数据中心采用最终一致性。
  • 例子:Google Spanner 和 Google Chubby 的设计。

ACID

ACID 是数据库事务的四个关键特性,保证了数据操作的 可靠性
它分别是:原子性 (Atomicity)、一致性 (Consistency)、隔离性 (Isolation)、持久性 (Durability)

  1. 原子性:事务中的所有操作要么全部成功要么全部不成功,通常采用Undo Log实现,来回滚失败的事务。
  2. 一致性:事务执行前后,数据库必须从一个 一致状态 转换到另一个 一致状态。所有数据必须满足数据库定义的约束(如主键唯一、外键约束、触发器、检查约束等)。假设数据库要求账户余额不能为负数,那么事务执行结束后,必须仍然满足这个规则。
  3. 隔离性:多个事务同时执行,每个事务应该是隔离的,数据库有不同的隔离等级。
  • 读未提交
  • 读已提交->解决脏读
  • 可重复读->解决不可重复度
  • 串行化->解决幻读
  1. 持久性:一旦事务提交成功,它对数据库的修改就是永久性的,即使系统崩溃也不会丢失。实现方式:数据库通常通过 WAL(Write Ahead Log,预写日志)Redo Log 来保证。

ACID和CAP的对比

原子性 (Atomicity, A)
所有系统都能从原子操作中获益。即使系统重点在于可用性,分区两边也应该继续使用原子操作。此外,更高层级的原子操作(ACID 所要求的那种)实际上能简化恢复过程。

一致性 (Consistency, C)
在 ACID 中,C 的含义是事务必须保持数据库的所有规则,例如唯一键约束。相比之下,CAP 中的 C 仅指 单副本一致性,这是 ACID 一致性的严格子集。ACID 的一致性无法跨分区维持——分区恢复时必须重新建立 ACID 一致性。更一般地说,在分区期间保持数据库不变式(invariants)可能是不可能的,因此必须仔细考虑:哪些操作需要禁止,以及在恢复时如何恢复这些不变式。

隔离性 (Isolation, I)
隔离性是 CAP 定理的核心:如果系统要求 ACID 的隔离性,在分区期间它最多只能在一侧运行。可串行化(serializability) 一般需要通信,因此无法跨分区维持。较弱的正确性定义在分区期间是可行的,可以通过分区恢复时的补偿机制来实现。

持久性 (Durability, D)
和原子性一样,没有理由放弃持久性,尽管开发者可能会为了降低代价而选择不依赖持久性,而是采用类似 BASE 的“软状态”。
一个微妙的问题是,在分区恢复过程中,可能需要撤销某些持久化操作,因为它们在执行时无意间违反了某个不变式。不过,在恢复时,由于两边都保留了持久化的历史记录,因此可以检测并修正这些操作。总体而言,在分区两侧都运行 ACID 事务可以使恢复更容易,并为补偿性事务(compensating transactions)提供框架,用于分区恢复。

BASE理论

BASE 理论的核心思想是:放弃强一致性,接受数据的短暂不一致,从而确保系统的高可用性和分区容错性

BASE 的含义

BASE 是一个缩写,它代表:

  1. Basically Available (基本可用)
    • 含义:分布式系统在出现不可预知的故障(如网络分区、节点宕机)时,系统保证核心功能依然可用,允许损失部分功能或体验(如响应时间变长、服务降级)。
    • 例子:双十一期间,为了应对海量请求,淘宝可能会关闭“查看历史订单详情”等非核心功能,或者将用户引导至一个降级页面,但核心的“下单、支付”功能必须保持可用。这牺牲了部分可用性,保证了核心可用,即“基本可用”。
  2. Soft State (软状态)
    • 含义:允许系统中的数据存在中间状态,并且这个状态即使没有新的输入,也可能随着时间而变化。这个状态是“软”的,因为它不需要在每一刻都完全准确,允许不同节点的数据副本之间存在暂时的不一致。
    • 例子:在微博中,你发布一条消息后,你的粉丝可能不会立刻看到。系统可能会需要几秒钟甚至几分钟的时间,将这条新微博异步地复制到所有粉丝的“信息流”缓存中。在这段复制时间内,数据就处于一种“软状态”——它确实存在,但并非所有用户都能立即访问到。
  3. Eventual Consistency (最终一致性)
    • 含义:这是 BASE 理论的最终目标。它保证如果系统在一段时间内没有收到新的数据更新操作,那么最终所有对数据的访问请求都会返回最新写入的值。也就是说,系统保证数据副本最终会达成一致,但不保证立即一致。
    • 例子:继续上面的微博例子。系统不保证你的粉丝A和粉丝B在同一秒看到你的新微博,但它保证,只要没有新的更新,在几秒或几分钟后,所有粉丝刷新他们的信息流时,最终都会看到这条微博。

一个经典的例子:电商库存

  • ACID 方式
    • 用户下单扣减库存时,数据库会立即锁定这条库存记录(隔离性),完成扣减和订单创建后(原子性)才释放锁。在此期间,其他所有用户看到的库存数都是扣减后的一致结果。这保证了不会超卖,但高并发时,锁竞争会导致性能瓶颈和响应延迟(可用性降低)。
  • BASE 方式
    • 用户下单时,系统先扣减缓存中的库存,并快速返回成功,允许用户下单(基本可用)。此时,缓存中的库存和数据库中的实际库存是不一致的(软状态)。
    • 系统通过一个后台任务,异步地将库存扣减操作最终同步到主数据库中(最终一致性)。
    • 这种方式吞吐量极高,用户体验好。但极端情况下(比如库存只剩1件,瞬间有大量请求),可能会发生“超卖”(数据短暂不一致的体现),这时系统通常通过后续的补救措施(如道歉、补偿)来处理。

总结

BASE 理论不是 ACID 的替代,而是一种补充和权衡。它是构建大型分布式系统时的一种设计哲学,其诞生源于对可用性分区容错性的追求,并坦然接受了由此带来的最终一致性后果。

超时

在1999年提出的经典CAP理论中,忽略了超时对分区的影响,实际上延迟与分区是紧密相关的
在运维层面,CAP 的核心体现于 超时 的那一刻:程序必须做出一个根本性的决定——即 分区决策

  • 取消操作,从而降低可用性,或
  • 继续执行操作,但冒着出现不一致的风险。

为了保证一致性而进行通信重试(例如使用 Paxos两阶段提交),其实只是延迟了这个决策。
最终,程序必须在某个时刻做出选择;而如果无限期地重试通信,本质上就是在 一致性 (C)可用性 (A) 之间选择了 一致性 (C)

其实分区就是超出了超时的网络,PAXOS协议会选择无限等待,这意味着PAXOS实际上选了CP而不是AP。

一致性

一致性的范围 指的是:在某个边界之内,系统状态是一致的,而超出该边界,就无法保证。例如,在一个主分区(primary partition)内部,可以同时保证一致性和可用性;但在分区之外,服务就不可用了。Paxos 和 原子多播(atomic multicast) 系统通常就是这种情况。以 Google 为例,主分区通常位于一个数据中心内部;而在广域范围内,则使用 Paxos 来确保全局共识(如 Chubby),以及高可用的持久化存储(如 Megastore)。

CAP的一致性是有边界的,例如RAFT协议中,访问没有来得及跟上主节点的从节点,这部分数据就是会不一致。

分支管理

在强一致性(C)的系统中时,我们选择放弃可用性(A),此时系统会自动保证事务或数据更新不会破坏一些不变式 (invariants)。这些不变式是强一致性保证的,

  • 如果选择 高可用 (A)(比如 BASE 模型中的 Eventually Consistent 系统),在网络分区时不同节点可能接受到不同的更新,导致系统短时间内违反不变式。
  • 当网络恢复后,就必须 恢复一致性,这时需要显式知道:
    • 哪些不变式被破坏了?
    • 如何恢复?
    • 需要做什么补偿操作?

👉 这就要求设计者必须 明确地列出并编码所有不变式,否则恢复阶段没法保证数据正确。

当我们选择AP的时候,很容易出现分支,如下图

image-20250924025901203

State从$S$演变出两个分支$S_1$和$S_2$,在解决网络分区后我们需要知道哪些分支是可以合并的。

分区模式后,有两种可能的策略:第一种是限制某些操作,从而降低可用性;第二种是记录关于操作的额外信息,这些信息将在分区恢复期间发挥作用。持续尝试通信将使系统能够辨别分区何时结束。

在分区发生时,我们要设计哪些操作是分区可以容忍的,而哪些操作不行,分区结束后,我们要对分区进行合并,以及补偿所产生的错误。

INRIA 的 Marc Shapiro 及其同事们的最新研究显著改进了利用可交换操作(commutative operations)来实现状态收敛的方法。团队提出了可交换复制数据类型(CRDTs),这是一类能够在网络分区后可证明地收敛的数据结构,并描述了如何使用这些结构来:

  • 确保在分区期间的所有操作都是可交换的,或者
  • 将值表示在一个**格结构(lattice)**上,并确保在分区期间的所有操作在该格上都是**单调递增**的。

后一种方法通过取每一方值的最大值来实现状态收敛。这是对亚马逊购物车做法的形式化和改进:在分区之后,最终收敛的结果是两个购物车的并集,而并集是一种单调的集合操作。这样做的后果是,被删除的商品可能会重新出现。

然而,CRDT 也能实现同时支持添加删除操作的分区容忍集合。其核心思想是维护两个集合:一个记录新增项,另一个记录删除项,最终的集合成员由两者的差集决定。每个简化后的集合都会收敛,因此它们的差集也会收敛。在某个时间点,系统可以通过将已删除的元素从两个集合中移除来进行清理。不过,这种清理通常只能在系统不发生分区时进行。换句话说,设计者必须在分区期间禁止或延迟部分操作,但这些仅仅是清理操作,不会影响用户感知的可用性。

因此,通过使用 CRDT 来实现状态,设计者可以在选择 高可用性 (A) 的同时,仍然确保系统在分区后能自动收敛


参考资料

  • Brewer, E. A. (2000). Towards Robust Distributed Systems. Proceedings of the 19th Annual ACM Symposium on Principles of Distributed Computing (PODC).

  • Gilbert, S., & Lynch, N. (2002). Brewer’s Conjecture and the Feasibility of Consistent, Available, Partition-tolerant Web Services. ACM SIGACT News, 33(2), 51–59.

  • Kleppmann, M. (2017). Designing Data-Intensive Applications: The Big Ideas Behind Reliable, Scalable, and Maintainable Systems. O’Reilly Media. Ch. 7-9.

  • Shapiro, M., Preguiça, N., Baquero, C., & Zawirski, M. (2011). Conflict-free Replicated Data Types. Informa CTM, Université Pierre et Marie Curie.

  • Amazon DynamoDB. Consistency Models. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.html

  • Lynch, N. A., & Gilbert, S. (2002). Impossibility Results for Distributed Computing. https://groups.csail.mit.edu/tds/papers/Lynch/podc-2002.pdf

ACID的设计是单机型数据库的设计原则,为了保证数据的强一致性,而BASE理论则是分布式系统/数据库设计原则,为了保证数据的高可用和高扩展,允许数据的短暂不一致,只需要保证最终一致性即可。


CAP理论
https://yicizhang00.github.io/posts/分布式/理论基础/CAP/
作者
Yici Zhang
发布于
2026年5月10日
许可协议