本文系统梳理分布式一致性的核心理论与协议:从 CAP/BASE 的设计哲学,到强/顺序/因果/最终四种一致性模型,再到 Paxos、Raft、Gossip、向量时钟等具体实现方案。读完能清楚知道在什么场景下选哪种一致性保证,以及各协议的工作原理与取舍。
相关文章:分布式事务 · 分布式协调与选举 · 分布式系统设计模式 · 金融级分布式系统实践
目录
| 章节 | 说明 |
|---|---|
| CAP 定理 | 三角权衡,分布式系统的 PH 试纸 |
| BASE 理论 | CAP 中 AP 的延伸,追求可用性 |
| 一致性模型 | 强/顺序/因果/最终一致性对比 |
| Paxos 协议 | Basic Paxos 两阶段 + Multi-Paxos |
| Raft 协议 | Leader 选举 + 日志复制 + 成员变更 |
| Gossip 协议 | 最终一致性的传播机制 |
| 向量时钟 | 因果关系的逻辑时序工具 |
CAP 定理
CAP 是分布式系统的 PH 试纸,帮助我们在一致性和可用性之间做出有依据的权衡。
三个指标
| 指标 | 含义 | 强调 |
|---|---|---|
| C(Consistency) | 每次读操作,要么读到最新写入的数据,要么返回失败 | 数据正确 |
| A(Availability) | 每次请求都能得到响应,但不保证是最新数据 | 服务可用 |
| P(Partition Tolerance) | 节点间出现消息丢失或高延迟时,系统仍能继续工作 | 容错能力 |
CAP 不可能三角
三个指标不可兼得,只能三选二。由于节点间的分区故障是分布式系统的必然现象,P 是必须保证的前提,因此实践中只剩 C 和 A 的取舍:
| 模式 | 含义 | 典型系统 |
|---|---|---|
| CP | 牺牲可用性,保证强一致性 | etcd、Consul、HBase、ZooKeeper |
| AP | 牺牲强一致性,保证高可用 | Cassandra、DynamoDB、Eureka |
| CA | 不存在于分布式系统(等同于单机) | 单机 MySQL |
常见误解:并非任何情况下都只能 C 和 A 选一个。在没有发生网络分区时(系统绝大多数时候的状态),C 和 A 可以同时保证。只有在网络分区发生时才需要二选一。
实践案例:InfluxDB 的 CAP 设计
- META 节点(存储数据库名、表名等关键元信息)→ 选 CP,采用 Raft 强一致性。元信息不一致会导致写入失败,代价极高。
- DATA 节点(存储时序数据)→ 选 AP,采用分片 + 多副本。时序数据追求高并发写入和水平扩展,允许短暂不一致。
BASE 理论
BASE 是 CAP 中 AP 的延伸,是对大规模互联网分布式系统实践的总结。核心思想:优先保证可用性,允许数据在一段时间内不一致,但最终达到一致。
三个核心概念
| 概念 | 含义 |
|---|---|
| Basically Available(基本可用) | 出现故障时,允许损失部分功能的可用性,保障核心功能可用 |
| Soft State(软状态) | 允许系统存在中间状态(不同节点数据副本短暂不一致),不影响整体可用性 |
| Eventually Consistent(最终一致性) | 系统中所有数据副本经过一段时间同步后,最终能达到一致状态 |
实现基本可用的四板斧
- 流量削峰:错开不同区域的访问请求(如 12306 按区域分时段售票)
- 延迟响应:请求排队处理,牺牲响应时间保核心功能
- 体验降级:用低质量替代高质量(如用小图替代原图)
- 过载保护:超时请求直接拒绝,队列满后清除排队请求
实现最终一致性的三种方式
| 方式 | 原理 | 性能消耗 | 推荐度 |
|---|---|---|---|
| 写时修复 | 写入时检测不一致并修复,无需对比 | 低 | 优先选择 |
| 读时修复 | 读取时检测不一致并修复(如 Cassandra Read Repair) | 中 | 次选 |
| 异步修复 | 定时对账检测副本差异(反熵) | 高 | 补充手段 |
ACID vs BASE:ACID 追求强一致性,适合金融等强事务场景;BASE 追求最终一致性,适合大规模互联网分布式系统。两者都保证持久性,是正确性与性能之间的不同权衡。
一致性模型
一致性模型是正确性与性能之间的权衡,强度越高实现成本越大。
四种经典模型(从强到弱)
| 模型 | 定义 | 特征 |
|---|---|---|
| 线性一致性(强一致性) | 写操作完成后,所有客户端立即能读到最新值;所有客户端看到的顺序符合时间先后 | 有全局时间顺序,最严格 |
| 顺序一致性 | 所有进程看到的操作顺序一致,但不要求符合真实时间顺序 | 有 total order,放松了时间约束 |
| 因果一致性 | 有因果关系的操作顺序在所有节点一致,无因果关系的并发操作可以不同顺序 | 只保证因果有序,无 total order |
| 最终一致性 | 所有副本在经过一段时间后最终达到一致,过程中可读到任意旧值 | 最宽松,性能最高 |
强度:线性一致性 > 顺序一致性 > 因果一致性 > 最终一致性
性能:线性一致性 < 顺序一致性 < 因果一致性 < 最终一致性
以用户为中心的一致性
| 模型 | 含义 | 场景 |
|---|---|---|
| 单调读一致性 | 读到新值后,不会再读到比它旧的值 | 防止数据"回退" |
| 单调写一致性 | 同一进程的写操作按顺序完成 | 顺序写入场景 |
| 写后读一致性 | 写入后,该进程的后续读一定能读到写入的值 | 主从读写分离时写主读主 |
| 读后写一致性 | 读到某值后的写操作,基于不低于该值的版本 | 防止基于过期数据写入 |
Paxos 协议
Paxos 是分布式共识的奠基性算法,过去几十年里是分布式共识的代名词。
Basic Paxos
目标:多个节点就单个值达成共识。
三种角色:
| 角色 | 职责 |
|---|---|
| Proposer(提议者) | 接收客户端请求,发起二阶段提交,协调共识 |
| Acceptor(接受者) | 对提议的值进行投票,存储接受的值 |
| Learner(学习者) | 被动接受达成共识的值,不参与投票(数据备份节点) |
两阶段流程:
sequenceDiagram
participant P as Proposer
participant A1 as Acceptor-1
participant A2 as Acceptor-2
participant A3 as Acceptor-3
Note over P,A3: 阶段一:Prepare(准备)
P->>A1: Prepare(n=5)
P->>A2: Prepare(n=5)
P->>A3: Prepare(n=5)
A1-->>P: Promise(尚无提案)
A2-->>P: Promise(尚无提案)
A3-->>P: Promise(尚无提案)
Note over P,A3: 阶段二:Accept(接受)
P->>A1: Accept[5, value=7]
P->>A2: Accept[5, value=7]
P->>A3: Accept[5, value=7]
A1-->>P: Accepted
A2-->>P: Accepted
A3-->>P: Accepted
Note over P,A3: 达成共识:value=7
Acceptor 的三个承诺:
- 若 Prepare 请求的提案编号 ≤ 已响应的最大编号,不响应该 Prepare
- 若 Accept 请求的提案编号 < 已响应的最大编号,不通过该提案
- 若之前有通过提案,在 Prepare 响应中包含已通过的最大编号提案信息
容错能力:少于一半的节点故障时,共识协商仍正常工作("大多数"原则)。
Multi-Paxos
目标:就一系列值达成共识(Basic Paxos 只能处理单个值)。
Basic Paxos 的两个痛点:
- 多个 Proposer 同时提案会导致提案冲突,需要重新协商
- 每次都需要 2 轮 RPC(Prepare + Accept),延迟高
Multi-Paxos 的两个改进:
| 改进 | 方式 | 效果 |
|---|---|---|
| 引入 Leader | 唯一 Proposer,消除提案冲突 | 无竞争,稳定 |
| 省略 Prepare 阶段 | Leader 稳定时直接进入 Accept 阶段 | 减少一半消息往返 |
重要:兰伯特的 Multi-Paxos 是思想而非完整算法,缺少选举 Leader 等实现细节。Chubby、Raft 等都是基于 Multi-Paxos 思想的具体实现。Multi-Paxos 的正确性未经严格证明,生产环境优先选择 Raft。
Raft 协议
Raft 是当前分布式系统开发的首选共识算法,以"一切以 Leader 为准"的方式实现共识,在可理解性上远优于 Paxos。
三种节点状态
flowchart LR
F[Follower<br/>跟随者] -->|超时未收到心跳| C[Candidate<br/>候选人]
C -->|获得多数票| L[Leader<br/>领导者]
C -->|发现更高任期| F
L -->|发现更高任期| F
style L fill:#cfc,stroke:#060
style C fill:#ffc,stroke:#660
style F fill:#eef,stroke:#66c
Leader 选举
流程:
- 初始所有节点为 Follower
- Follower 等待心跳超时(随机超时时间,150~300ms),转为 Candidate
- Candidate 自增任期编号,给自己投票,向其他节点发送 RequestVote RPC
- 获得大多数选票的 Candidate 成为 Leader,开始发送心跳
选举规则:
- 每个任期每个节点只能投一票(先来先服务)
- 日志完整性高的节点(最后一条日志项任期编号更大,或索引更大)才能当选 Leader
- 随机超时时间减少选票瓜分,保证大多数情况只有一个节点先发起选举
任期(Term):单调递增的整数,用于检测过期信息。发现自己任期编号比其他节点小,立即降为 Follower。
日志复制
日志项结构:[索引值, 任期编号, 指令]
sequenceDiagram
participant C as Client
participant L as Leader
participant F1 as Follower-1
participant F2 as Follower-2
C->>L: 写请求(SET x=3)
L->>L: 追加日志项
L->>F1: AppendEntries RPC
L->>F2: AppendEntries RPC
F1-->>L: 复制成功
F2-->>L: 复制成功
Note over L: 大多数节点确认,提交日志
L->>L: 应用到状态机
L-->>C: 返回成功
Note over F1,F2: 下次心跳时,<br/>Follower 得知提交位置,<br/>应用到自己的状态机
日志一致性修复:Leader 通过一致性检查找到与 Follower 相同日志的最大索引,然后强制覆盖之后的不一致日志项。Leader 从不覆盖自己的日志。
成员变更
直接从旧配置切换到新配置可能导致同一任期出现两个 Leader(脑裂)。Raft 通过**联合共识(Joint Consensus)**解决:先进入同时使用新旧两个配置的过渡状态,再切换到新配置。
Raft vs Paxos 对比
| 维度 | Raft | Multi-Paxos |
|---|---|---|
| 可理解性 | 高,限制多(日志必须连续) | 低,灵活但复杂 |
| 日志要求 | 必须连续 | 不要求连续 |
| Leader 选举 | 明确定义 | 未定义,需自行实现 |
| 正确性证明 | 已证明 | 未完整证明 |
| 生产应用 | etcd、Consul、CockroachDB | Chubby、Spanner(早期) |
Gossip 协议
Gossip(流言蜚语)协议通过随机、传染性的方式将信息传播到整个网络,实现最终一致性。适合极端情况下(如集群只剩一个节点)也需要运行的系统。
三板斧
| 方式 | 原理 | 特点 |
|---|---|---|
| 直接邮寄(Direct Mail) | 直接发送更新数据,失败时缓存重传 | 实时性高,但可能因缓存满而丢数据,单独使用无法保证最终一致性 |
| 反熵(Anti-entropy) | 节点定期随机选择另一节点,互相交换所有数据消除差异 | 可靠,但通信成本高(节点两两对比),适合节点数量固定的系统 |
| 谣言传播(Rumor mongering) | 有新数据的节点变为活跃状态,周期性向其他节点扩散,直到所有节点都有该数据 | 传染性强,适合动态变化的分布式系统 |
反熵的三种实现方式
sequenceDiagram
participant A as 节点A
participant B as 节点B
Note over A,B: 推方式(Push)
A->>B: 发送自己的全部副本数据
Note over B: 修复自己的熵
Note over A,B: 拉方式(Pull)
A->>B: 请求对方的全部副本数据
B-->>A: 返回数据
Note over A: 修复自己的熵
Note over A,B: 推拉方式(Push-Pull)
A->>B: 互相交换数据
B-->>A: 互相交换数据
Note over A,B: 双方都修复熵
实践建议:直接邮寄必须实现(低开销);节点固定时用反熵;节点动态变化时用谣言传播。反熵建议引入校验和(Checksum)降低数据对比量,并做成可配置开关。
向量时钟
向量时钟(Vector Clock)是一种用于追踪分布式系统中事件因果关系的逻辑时钟,解决"哪个事件先发生"的问题。
核心思想
每个节点维护一个向量 [t1, t2, ..., tn],其中 ti 表示节点 i 的本地逻辑时间。
更新规则:
- 节点执行本地事件:自增自己的计数器
- 节点发送消息:附带当前向量时钟
- 节点接收消息:取每个分量的最大值,再自增自己的计数器
因果关系判断:
- 事件 A 的向量时钟在每个分量上都 ≤ 事件 B → A 发生在 B 之前(A → B)
- 两个事件互不支配 → 并发事件,无因果关系
与物理时钟的对比
| 维度 | 物理时钟 | 向量时钟 |
|---|---|---|
| 同步要求 | 需要全局时钟同步 | 不需要 |
| 因果判断 | 不可靠(时钟漂移) | 精确 |
| 开销 | 低 | O(n),n 为节点数 |
| 典型应用 | 日志时间戳 | Dynamo、Riak 冲突检测 |
应用场景:Dynamo 使用向量时钟检测多副本数据冲突,当检测到并发写入时,将冲突数据交给应用层(或客户端)解决。
参考资料
- 《分布式协议与算法实战》— 韩健(极客时间)
- 《分布式技术原理与算法解析》— 聂鹏程(极客时间)
- 《深入浅出分布式技术原理》— 陈现麟(极客时间)
- In Search of an Understandable Consensus Algorithm (Raft)
- Brewer's Conjecture and the Feasibility of Consistent Available Partition-Tolerant Web Services
评论 (0)
发表评论