---
title: CAP 与一致性模型
url: https://doc.liz6.com/distributed-systems/01-basic-theory/01-cap-and-consistency-models
locale: zh
area: distributed-systems
tags:
- distributed-systems
- 基础理论
date: 2026-06-30
modified: 2026-07-11
description: CAP 不是"三选二"——网络分区不可避免,发生时必须在一致性和可用性之间抉择。从 linearizability 到 eventual,一致性不是开关而是一条光谱,每种选择都有对应的工程代价和适用场景。
---

# 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 之前。

<svg viewBox="0 0 720 300" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="Linearizability 时间线:操作的全局顺序必须与实时时间保持一致">
  <defs>
    <marker id="arrow-linearizability" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker>
  </defs>
  <rect width="720" height="300" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">Linearizability:操作顺序必须与实时时间一致</text>

  <text x="40" y="105" font-size="13" font-weight="700" fill="#1f2933">Client1</text>
  <rect x="120" y="82" width="110" height="36" rx="6" fill="#4f46e5"/>
  <text x="175" y="105" text-anchor="middle" font-size="12" font-weight="600" fill="#ffffff">write(x=1)</text>
  <rect x="400" y="82" width="110" height="36" rx="6" fill="#4f46e5"/>
  <text x="455" y="105" text-anchor="middle" font-size="12" font-weight="600" fill="#ffffff">write(x=2)</text>
  <line x1="175" y1="118" x2="175" y2="130" stroke="#94a3b8" stroke-width="1.4" stroke-dasharray="3 2"/>
  <line x1="455" y1="118" x2="455" y2="130" stroke="#94a3b8" stroke-width="1.4" stroke-dasharray="3 2"/>
  <text x="175" y="140" text-anchor="middle" font-size="10" fill="#64748b">t1</text>
  <text x="455" y="140" text-anchor="middle" font-size="10" fill="#64748b">t2</text>
  <line x1="175" y1="140" x2="175" y2="205" stroke="#e2e8f0" stroke-width="1.4" stroke-dasharray="3 3"/>
  <line x1="455" y1="140" x2="455" y2="205" stroke="#e2e8f0" stroke-width="1.4" stroke-dasharray="3 3"/>

  <text x="40" y="175" font-size="13" font-weight="700" fill="#1f2933">Client2</text>
  <rect x="230" y="152" width="110" height="36" rx="6" fill="#0d9488"/>
  <text x="285" y="175" text-anchor="middle" font-size="12" font-weight="600" fill="#ffffff">read(x)</text>
  <rect x="560" y="152" width="90" height="36" rx="6" fill="#22c55e"/>
  <text x="605" y="175" text-anchor="middle" font-size="12" font-weight="600" fill="#ffffff">read(x)</text>

  <line x1="285" y1="188" x2="285" y2="206" stroke="#475569" stroke-width="1.6" marker-end="url(#arrow-linearizability)"/>
  <line x1="605" y1="188" x2="605" y2="206" stroke="#475569" stroke-width="1.6" marker-end="url(#arrow-linearizability)"/>
  <text x="285" y="222" text-anchor="middle" font-size="11" fill="#0f766e">夹在 t1、t2 之间</text>
  <text x="285" y="238" text-anchor="middle" font-size="11" font-weight="700" fill="#115e59">→ 必须返回 1</text>
  <text x="605" y="222" text-anchor="middle" font-size="11" fill="#15803d">落在 t2 之后</text>
  <text x="605" y="238" text-anchor="middle" font-size="11" font-weight="700" fill="#166534">→ 必须返回 2</text>

  <rect x="60" y="252" width="600" height="36" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="76" y="274" font-size="12.5" fill="#3730a3">全局顺序必须与实时时间一致——这正是 linearizability 比 sequential consistency 更强的地方。</text>
</svg>

满足 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*
