第九章:一致性与共识
这一章是全书理论难度最高的一章,也是分布式系统的核心命题。第八章揭示了分布式系统的不确定性——网络不可靠、时钟不可靠、进程会暂停、没有全局真相。第九章则要回答:在这样一片混乱之上,我们究竟能建立什么样的保证?
Kleppmann 的论点是:一致性不是单一概念,而是一个从强到弱的谱系;共识是分布式系统中最强大的原语,但代价高昂,且不能解决所有问题。
本章结构:先讲线性一致性(最强的一致性模型),再讲顺序与因果性,然后讲共识问题及其算法,最后讲分布式事务与协调服务。
一、一致性模型的谱系
1. 为什么需要一致性模型
在分布式系统中,"一致性"这个词被滥用得很厉害。它可能指:
- 副本一致性:多个副本之间的数据是否相同
- 事务隔离性:并发事务之间的可见性
- 线性一致性:单一对象的强一致性保证
- 共识:多个节点对某个值达成一致
本章聚焦的是分布式系统层面的一致性,即多个副本、多个节点之间的保证。
2. 一致性模型的强度谱系
线性一致性(Linearizability)—— 最强 ↑ 顺序一致性(Sequential Consistency) ↑ 因果一致性(Causal Consistency) ↑ 写后读 / 单调读 / 一致前缀读 ↑ 最终一致性(Eventual Consistency)—— 最弱越强的一致性:
- 越容易编程
- 代价越高(延迟、可用性)
- 越难实现
二、线性一致性(Linearizability)
1. 定义
线性一致性,也叫原子一致性(Atomic Consistency)、强一致性(Strong Consistency)、外部一致性(External Consistency)。
核心思想:
系统看起来像是只有一个副本,所有操作都是原子的,且按某个全局顺序执行,这个顺序与真实时间一致。
更精确地说:
- 每个操作在调用和返回之间某个时间点"生效"
- 如果操作 A 在操作 B 开始之前完成,那么 A 必须在 B 之前生效
- 所有节点看到相同的操作顺序
2. 线性一致性的直观理解
例子:
- 用户 A 写入 x=1,写入成功后
- 用户 B 立即读取 x,必须读到 1
- 不能出现 B 读到旧值的情况
线性一致性 vs 可串行化:
- 可串行化:事务的隔离级别,保证并发事务等同于串行执行
- 线性一致性:单一对象的读写保证,保证操作按真实时间排序
- 两者可以结合:严格可串行化(Strict Serializability)
3. 线性一致性的代价
(1)性能
- 需要协调,延迟高
- 通常需要共识算法(Paxos、Raft)
- 跨数据中心线性一致性延迟极高
(2)可用性
- CAP 定理:网络分区时,线性一致性必须牺牲可用性
- 这是不可避免的权衡
(3)实现复杂
- 需要处理网络分区、时钟、进程暂停
- 需要共识算法
4. 线性一致性的应用场景
- 分布式锁:必须线性一致
- 唯一性约束:如用户名注册
- 领导者选举:必须线性一致
- 金融交易:强一致性要求
Kleppmann 的提醒:
线性一致性很强,但不是所有场景都需要。很多场景可以用更弱的一致性,换取更高的性能和可用性。
三、顺序一致性与因果一致性
1. 顺序一致性(Sequential Consistency)
定义:
所有节点看到相同的操作顺序,但这个顺序不必与真实时间一致。
与线性一致性的区别:
- 线性一致性:顺序必须与真实时间一致
- 顺序一致性:只要所有节点看到相同顺序即可
例子:
- 线性一致性:A 写 x=1,B 立即读,必须读到 1
- 顺序一致性:A 写 x=1,B 读可能读到旧值,但所有节点看到的顺序一致
2. 因果一致性(Causal Consistency)
定义:
只保证因果相关的操作按顺序被看到,无因果关系的操作可以任意顺序。
因果关系的例子:
- A 发帖,B 回复。B 的回复因果依赖于 A 的帖子。
- 所有节点必须先看到 A 的帖子,再看到 B 的回复。
因果一致性的优势:
- 比线性一致性弱,性能更好
- 比最终一致性强,避免因果错乱
- 是很多系统的实用选择
实现:
- 版本向量(Version Vectors)
- 因果依赖追踪
Kleppmann 的观点:
因果一致性是"最弱的有用一致性"。它避免了因果错乱,又不牺牲太多性能。
3. 因果顺序与全序
全序(Total Order):
- 所有事件可以比较先后
- 线性一致性需要全序
偏序(Partial Order):
- 只有部分事件可以比较
- 因果一致性只需要偏序
关键洞察:
分布式系统中,建立全序需要共识,代价高。很多时候,偏序(因果顺序)就足够了。
四、共识问题
1. 什么是共识
共识(Consensus):多个节点对某个值达成一致。
形式化:
- 所有节点提议一个值
- 最终所有节点决定同一个值
- 这个值必须是某个节点提议的
共识的性质:
- 一致性(Agreement):所有节点决定相同的值
- 有效性(Validity):决定的值必须是某个节点提议的
- 终止性(Termination):所有节点最终都会决定
- 完整性(Integrity):每个节点只决定一次
2. 共识的困难
FLP 不可能定理:
在异步系统中,即使只有一个节点可能崩溃,也不存在一个确定性的共识算法能保证终止。
意味着:
- 纯异步系统无法保证共识
- 实际系统需要部分同步假设(超时、时钟)
- 或者接受概率性终止
3. 共识算法
(1)Paxos
- Lamport 提出,最经典的共识算法
- 角色:Proposer、Acceptor、Learner
- 两阶段:Prepare、Accept
- 优点:理论完备
- 缺点:难理解、难实现
(2)Raft
- 为可理解性设计
- 角色:Leader、Follower、Candidate
- 强领导者:所有写经过 Leader
- 阶段:选举、日志复制
- 代表:etcd、Consul、TiKV
(3)ZAB(ZooKeeper Atomic Broadcast)
- ZooKeeper 使用
- 类似 Raft,但有细微差别
- 用于分布式协调
(4)拜占庭容错(BFT)
- 处理恶意节点
- PBFT、PoW、PoS
- 区块链场景
4. 共识的应用
- 领导者选举:选出唯一主节点
- 分布式锁:保证互斥
- 原子提交:分布式事务
- 配置管理:ZooKeeper、etcd
- 成员管理:集群成员变更
五、分布式事务与两阶段提交
1. 分布式事务的挑战
- 事务跨多个节点
- 需要原子性:要么全部提交,要么全部回滚
- 网络分区、节点故障使问题复杂
2. 两阶段提交(2PC, Two-Phase Commit)
阶段一:准备(Prepare)
- 协调者问所有参与者:“能提交吗?”
- 参与者回复"可以"或"不行"
阶段二:提交/回滚(Commit/Abort)
- 如果所有参与者都说"可以",协调者决定提交
- 否则回滚
- 协调者通知所有参与者
问题:
- 阻塞:参与者准备后,必须等待协调者决定
- 协调者故障:参与者可能永远等待
- 性能:需要多次网络往返
Kleppmann 的批评:
2PC 是一个阻塞式原子提交协议。它在协调者故障时会阻塞,这是它最大的问题。
3. 三阶段提交(3PC)
- 增加一个阶段,减少阻塞
- 但仍不能完全避免
- 实际使用较少
4. 共识与 2PC 的关系
- 2PC 不是共识算法
- 但它可以用共识算法实现(如 Raft + 2PC)
- 现代分布式数据库常用共识 + 复制状态机替代 2PC
5. 分布式事务的替代方案
- Saga:长事务拆成多个本地事务,用补偿操作回滚
- TCC:Try-Confirm-Cancel
- 最终一致性:接受暂时不一致
六、协调服务与成员管理
1. ZooKeeper 与 etcd
功能:
- 分布式配置管理
- 命名服务
- 分布式锁
- 领导者选举
- 成员管理
核心:
- 基于共识算法(ZAB、Raft)
- 提供线性一致性保证
- 小数据量,高可用
2. 成员管理
问题:
- 集群中哪些节点是活的?
- 节点加入/离开如何处理?
- 网络分区时如何判断?
解决:
- 用共识算法维护成员列表
- 心跳 + 超时
- 但超时无法区分"慢"和"死"
3. 协调服务的限制
- 不适合存储大量数据
- 不适合高吞吐
- 是系统的关键路径,故障影响大
Kleppmann 的提醒:
协调服务是分布式系统的"大脑",但它本身也是分布式系统,也需要容错。不要把它当作万能药。
七、本章的核心思想总结
| 主题 | 核心观点 |
|---|---|
| 线性一致性 | 最强一致性,代价高,需要共识 |
| 顺序一致性 | 所有节点看到相同顺序,不必与真实时间一致 |
| 因果一致性 | 最弱的有用一致性,避免因果错乱 |
| 共识 | 分布式系统最强原语,FLP 定理限制其终止性 |
| 共识算法 | Paxos、Raft、ZAB、BFT |
| 2PC | 阻塞式原子提交,协调者故障时阻塞 |
| 协调服务 | ZooKeeper、etcd,基于共识,是系统的关键路径 |
三个贯穿全书的判断:
- 一致性是谱系,不是二元:从线性一致到最终一致,选哪个取决于业务需求。
- 共识是分布式系统的核心原语:领导者选举、分布式锁、原子提交都依赖它。
- 共识不是免费的:它需要多数派、网络往返、持久化,代价高昂。
八、这一章在全书中地位
第九章是分布式系统理论的顶峰:
- 第五章数据复制:复制模型需要一致性保证
- 第六章数据分区:分区映射的协调需要共识
- 第七章事务:分布式事务需要原子提交
- 第八章分布式系统挑战:网络、时钟、进程暂停是共识要解决的问题
- 第十章批处理、第十一章流处理:大规模数据处理依赖共识做协调
一句话概括本章:
一致性是一个从线性一致到最终一致的谱系,线性一致性最强但代价最高。共识是分布式系统中最强大的原语,用于领导者选举、分布式锁和原子提交,但受 FLP 定理限制,需要部分同步假设。2PC 是阻塞式原子提交协议,协调者故障时会阻塞。协调服务(ZooKeeper、etcd)基于共识,是分布式系统的关键路径。理解这些,才能设计出真正可靠的分布式系统。