本文是《Designing Data-Intensive Applications》(DDIA,中文译名《数据密集型应用系统设计》)第 9 章的导读。DDIA 是 Martin Kleppmann 所著的分布式系统经典,本系列逐章导读,把书的核心概念讲清楚。
一句话主旨
分布式系统的"一致性"有两层意思,别混淆——一是"副本之间的数据一致性"(多个副本读到同样的值),二是"共识"(一群节点对某个决定达成一致)。本章把两者拧在一起:要实现强一致性(线性一致性),就需要共识协议(Raft/Paxos);而共识协议在网络分区时必须牺牲可用性——这就是 CAP 定理的根源。这章是全书最硬、价值最高的钉子户,因为它解释了"为什么分布式系统没有银弹"。
核心概念拆解
1. 先分清两种"一致性"
这是读本章的第一个障碍——中文都叫"一致性",英文不同,含义不同:
| 一致性(Consistency, 副本一致性) | 共识(Consensus) | |
|---|---|---|
| 问什么 | “多个副本读到一样的值吗?” | “一群节点对某个决定达成一致了吗?” |
| 范围 | 数据复制(第 5 章) | 决策达成(选主、提交、配置变更) |
| 关系 | 强一致性(线性一致)需要共识来保证 | 共识是手段,一致性是目的 |
CAP 里的 C 是副本一致性(线性一致性),不是 ACID 里的 C(业务约束一致性)。这两个 C 完全不同,是分布式系统术语最坑的混淆点。ACID 的 C 是应用层不变量;CAP 的 C 是"读总是看到最新写"的强一致。
2. 线性一致性(Linearizability)——最强的副本一致性
直觉:尽管有多个副本和并发操作,系统看起来就像"只有一个副本"——每个操作在某个瞬时点原子完成,所有观察者看到同一个全局顺序。
线性一致性的代价:要保证"读看到最新写",读写都要涉及多数派(quorum),不能只读本地副本。这意味着每次操作都有跨节点通信延迟,且网络分区时无法满足(少数派节点无法凑够 quorum,只能拒绝服务 → 牺牲可用性)。
第 5 章的 Quorum(w+r>n)是实现线性一致性的基础,但 Quorum 本身不够——还要保证读到的最新版本能被识别(不能读到"旧但凑够 r 的版本"),这需要共识协议协调。所以线性一致性需要在 Quorum 基础上协调版本顺序。
3. 因果一致性——比线性弱,但不需共识
直觉:不保证全局单一顺序,但保证"有因果关系的操作按因果序可见"。无因果关系的并发操作,不同观察者看到的顺序可以不同。
- A→B 有因果:所有观察者必须先看到 A 再看到 B
- A 和 C 并发:有人可以先看到 C 再看到 A,可以
因果一致性不需要共识(只靠逻辑时钟/版本向量追踪因果),所以在网络分区时仍可用——这是它的优势。但实现复杂度高,很多系统默认不做(要么接受线性要么接受最终一致)。
4. CAP 定理——被滥用但核心正确
CAP 的准确表述:在网络分区(Partition)发生时,你只能在一致性(C,线性一致性)和可用性(A)之间二选一。
关键澄清(CAP 最常被误解的点):
- CAP 只在网络分区时要求二选一。没有分区时,C 和 A 可以同时有。
- CAP 的 C 是线性一致性,不是 ACID 的 C,也不是"最终一致性"。
- CAP 不是"三选二",是"分区时 C 和 A 选一个"。平时三个都能要。
- 分区不算罕见(网络抖动、节点宕机、GC 停顿都算分区),所以现实里更多是 PACELC:无分区时也要在延迟(L)和一致性(C)间权衡。
PACELC 更贴近现实:P(分区)时选 A 还是 C;E(else,无分区)时选 L(低延迟)还是 C(强一致)。元数据系统通常选 PC+EC(分区时选一致牺牲可用,平时也选一致牺牲延迟——元数据不能丢);数据/检索层通常选 PA+EL(分区时选可用,平时选低延迟——检索要快不要等 quorum)。两个选择都正确,因为服务的对象不同:元数据错不起,检索慢不起。
5. 共识协议——Raft / Paxos
共识要解决的问题:一群节点对某个值/决定达成一致,即使有节点宕机、网络延迟/分区。这是分布式系统最核心的难题。
最常见的共识任务:选主(Leader Election)——多个节点就"谁是 Leader"达成一致。
Raft——现代共识协议,易理解
直觉:一个 Leader 接收所有写,把日志复制到 Followers,靠"多数派确认"提交。Leader 挂了,多数派选新 Leader。
Raft 的关键机制:
① Term(任期):时间被分成 term,每个 term 最多一个 Leader。term 号单调递增,是 Raft 的"逻辑时钟"——过期的 Leader(旧 term)的请求会被拒绝。
② 选主:Leader 挂了,Follower 超时未收到心跳 → 自己变 Candidate,term+1,向其他节点拉票。获得多数派票就成新 Leader。
③ 日志复制:Leader 把写操作写入日志,复制到 Followers,多数派确认后提交(commit)。已提交的日志保证不丢(只要多数派存活)。
④ 安全性:Raft 保证"已提交的日志在新 Leader 上一定存在"——选主时,Candidate 的日志必须至少和多数派一样新,否则选不上。这保证了 committed 日志不丢。
共识协议要求多数派确认,在资源紧张/网络问题时凑不够多数派,系统就会卡住或拒绝服务。这是 CP 设计的预期行为——强一致性系统宁可不可用也不冒不一致风险。理解这点,遇到"系统卡住但重启后恢复"的情况,就知道可能是共识协议在压力下的超时卡住,而非数据损坏。
问题→方案:问题——分布式环境下节点会宕机、网络会分区,如何让多个节点对同一个决定达成一致。场景——选主、日志提交、跨节点事务,都需要"多数同意才算数"。方案——共识协议(Raft/Paxos):Leader 接收写请求,复制到多数派确认后提交,Leader 挂了多数派重选举。代价是网络分区时凑不够多数派就拒绝服务(CAP 的 CP 选择)。线性一致性需要在 Quorum 基础上协调版本顺序;因果一致性是更弱的替代,不需共识但实现复杂。
Paxos——经典但难懂
Raft 是为"易理解"设计的,Paxos 是 Raft 之前的经典协议,数学上优雅但出了名难懂。不需要懂 Paxos 细节,只需知道:Raft 和 Paxos 解决同一个问题(共识),Raft 是 Paxos 的工程化简化版,现代系统(etcd、Consul 等)大多选 Raft。
6. 两阶段提交(2PC)——跨节点事务的原子提交
直觉:一个事务要写多个节点,要么全提交要么全回滚。2PC 用一个协调者(Coordinator)分两阶段:
2PC 的致命弱点——协调者故障:阶段 1 后、阶段 2 前,协调者挂了 → 节点们锁着资源等指令,不知道该提交还是回滚 →阻塞。这就是 2PC 的"阻塞问题"。
2PC 是解决跨节点事务的标准方案,但它的协调者单点和阻塞是已知缺陷。现代系统用 Raft 让协调者本身也高可用(如改进版 2PC + 共识选协调者)来缓解。
Mermaid:CAP / PACELC 决策
章末分层诊断框架(本系列补充,非原书内容)
遇到"分布式系统在异常下卡住/崩溃"时,用这个框架诊断:
- 是底层共识协议没收敛?(如选主卡住,凑不够多数派)→ 等网络/资源恢复或重启节点重选举
- 还是上层状态机逻辑有缺陷?(如分配/重平衡逻辑死循环)→ 修应用逻辑或排查应用逻辑
两者症状相似(都表现为"卡住/拒绝服务"),但根因和修法不同。底层共识卡住通常是资源/网络问题,重启可恢复(状态没坏);上层逻辑缺陷可能需要修代码或排查应用逻辑。
下一篇:第 10 章——批处理。进入第三部分(派生数据),从"分布式理论"转向"数据怎么用批/流/集成串起来"。