分布式共识:Paxos、Raft、ZAB 与多数派
共识算法解决的是:多个节点可能宕机、网络可能分区、消息可能延迟和乱序时,幸存节点怎样对一串操作的顺序和结果达成一致。它是 etcd、Consul、ZooKeeper、KRaft 元数据控制器等协调系统的重要基础,但不是业务开发中所有一致性问题的通用替代品。
本章以崩溃故障和非拜占庭网络模型为主。经典 Paxos、Raft、ZAB 通常不处理节点恶意伪造消息的拜占庭故障;若业务需要抵抗恶意节点,要研究 PBFT、HotStuff 等拜占庭容错协议,不能直接套用本章结论。
学习路径:本页建立 Paxos、Raft、ZAB 与 Quorum 主线;随后进入 Raft 内部原理与生产治理,从 etcd/raft 的 Tick、Step、Ready、Progress、Commit、ReadIndex、Snapshot 和 Joint Consensus 追到真实三节点故障测试;面试标准回答单独放在 共识算法与 Raft 独立面试题。
一、学习目标
学完后应能解释:
- 共识、复制、事务、分布式锁分别解决什么问题。
- Safety 与 Liveness 为什么要分开讨论。
- 多数派为什么要求集合相交,3/5/7 节点分别能容忍多少故障。
- Raft 的 Follower、Candidate、Leader、Term 和 Log 是什么。
- RequestVote、AppendEntries 如何完成选主、心跳和日志复制。
- 旧 Leader 在网络分区后为什么不能仅凭本地状态继续提交。
- 日志冲突怎样通过 prevLogIndex/prevLogTerm 检测和回退。
- Raft 何时能认为日志已提交,为什么当前任期提交规则很重要。
- Paxos 的 Proposer、Acceptor、Learner 和两阶段消息是什么。
- Multi-Paxos 为什么通常需要稳定 Leader 降低成本。
- ZAB 的选主恢复、数据同步和原子广播如何工作。
- 快照、日志压缩、成员变更和线性一致读为什么不能省略。
- ZooKeeper、etcd、Consul、Kafka KRaft 和普通 Redis 锁的边界。
二、共识到底在“共识”什么
对复制状态机而言,各节点不是直接讨论最终数据库长什么样,而是先对操作日志的顺序达成一致:
index=1: SET config.version = 7
index=2: ADD member node-4
index=3: DELETE lock/order-1001只要每个正确节点以相同顺序执行相同确定性命令,就能得到相同状态。
flowchart TD
A["客户端提交命令"] --> B["共识层决定日志位置和任期"]
B --> C["日志复制到多数节点"]
C --> D["日志被标记为已提交"]
D --> E["各节点按相同顺序应用到状态机"]
E --> F["得到一致的配置、成员或元数据"]共识不直接保证:
- 订单库和支付库跨服务事务自动一致。
- 一个 HTTP 请求不会被重复执行。
- 缓存与数据库自动同步。
- 业务补偿一定正确。
- 客户端在超时后知道命令是否已经提交。
这些还需要事务、幂等、状态机、消息和查询确认。
三、Safety 与 Liveness
| 属性 | 要求 | 失败示例 |
|---|---|---|
| Safety 安全性 | 不会出现两个互相冲突的已提交结果 | 两个分区都确认自己成功写入同一日志位置的不同命令 |
| Liveness 活性 | 条件恢复后,合法请求最终能继续完成 | 网络恢复且多数节点健康,集群仍永久无法选主 |
安全性通常要求任何时刻都不能被破坏;活性依赖时序和网络最终恢复。网络完全异步且消息可无限延迟时,无法仅凭超时完美区分“节点宕机”和“节点很慢”。实际协议依赖随机选举超时、心跳和“最终存在一段足够稳定的通信期”取得进展。
因此发生分区时,少数派通常宁可停止写,也不能确认冲突结果;这就是 CP 协调系统常见行为。
四、先确认故障模型
本章主要讨论:
- Crash-stop:节点停止并不再恢复。
- Crash-recovery:节点重启并从持久化状态恢复。
- 消息丢失、重复、延迟、乱序。
- 网络分区。
通常假设:节点不会恶意发送伪造内容,磁盘持久化满足协议要求,消息可被校验来源和格式。若磁盘写成功实际未持久化、节点返回虚假 ACK、证书被盗或实现有 Bug,经典 crash-fault 共识结论不能自动兜底。
五、多数派与 Quorum 为什么有效
N 个投票节点的多数派大小:
quorum = floor(N / 2) + 1| 节点数 | 多数派 | 可容忍同时故障数 |
|---|---|---|
| 1 | 1 | 0 |
| 3 | 2 | 1 |
| 5 | 3 | 2 |
| 7 | 4 | 3 |
关键不是“多数听起来民主”,而是任意两个严格多数派必然至少有一个交集节点。
flowchart TD
A["旧多数派:节点 A、B、C"] --> C["交集节点 C"]
B["新多数派:节点 C、D、E"] --> C
C --> D["新一轮决策有机会得知旧已提交信息"]对于 2f + 1 个节点,通常可容忍 f 个崩溃故障并保持多数派。偶数节点往往不会提高同等级故障容忍:4 节点需要 3 票,只能容忍 1 个;3 节点需要 2 票,也容忍 1 个,却少一个节点成本。
多数派只是协议的一部分。若投票规则、日志新旧判断、持久化或成员变更实现错误,即使数票也可能不安全。
六、网络分区与脑裂
5 节点分成 3 节点和 2 节点两个分区:
flowchart TD
A["网络分区"] --> B["分区一:3个投票节点"]
A --> C["分区二:2个投票节点"]
B --> D["可以形成多数派并选主、提交"]
C --> E["无法形成多数派,必须停止提交"]少数派旧 Leader 可能仍认为自己是 Leader,但它收不到多数 ACK,不能推进 commitIndex。客户端若只看“节点角色”而不等提交确认,可能误以为写成功。
脑裂防护不能只靠“检测到两个 Leader”。在不同 Term 的短暂窗口内看到两个自称 Leader 并不一定违反安全性;关键是同一日志位置不能有两个冲突条目都被合法提交。
七、Raft 总体模型
Raft 将共识拆成三个主要问题:
- Leader Election:选出 Leader。
- Log Replication:由 Leader 复制日志。
- Safety:保证已提交日志不会被后续 Leader 覆盖。
节点状态:
| 状态 | 行为 |
|---|---|
| Follower | 接收 Leader 日志和心跳;超时后发起选举 |
| Candidate | 增加 Term,投自己并向其他节点请求投票 |
| Leader | 接收客户端命令、追加日志、复制并推进提交 |
每个节点持久化或维护的重要状态通常包括:
currentTerm:见过的最高任期。votedFor:当前任期投给谁。log[]:日志条目,每条有 index、term、command。commitIndex:已知已提交的最高日志位置。lastApplied:已应用到状态机的最高位置。- Leader 对每个 Follower 的
nextIndex、matchIndex。
八、Term 为什么重要
Term 是逻辑任期,可理解为一次选举时代编号:
Term 1:node-A 是 Leader
Term 2:选举未成功
Term 3:node-C 是 Leader规则:
- 节点发起选举前增加 currentTerm。
- RPC 带上发送方 Term。
- 节点看到更高 Term,应更新本地 Term 并退回 Follower。
- 低 Term 请求会被拒绝。
Term 让节点识别过期 Leader 和过期消息,但仅比较 Term 仍不够;投票时还要比较候选日志是否至少和自己一样新。
九、Raft 选主全过程
flowchart TD
A["Follower 在随机选举超时内未收到有效心跳"] --> B["currentTerm 加 1"]
B --> C["切换 Candidate 并投自己"]
C --> D["并行发送 RequestVote"]
D --> E["接收方比较 Term 和候选日志新旧"]
E --> F{"获得多数票"}
F -- "是" --> G["成为 Leader 并立即发送心跳"]
F -- "否且发现更高Term" --> H["退回 Follower"]
F -- "选举超时" --> I["新一轮随机超时与更高Term"]随机选举超时用于降低多个 Follower 同时成为 Candidate、平分选票的概率。心跳间隔应明显小于选举超时;超时过短会因短暂 GC、网络抖动频繁换主,过长会增加故障恢复时间。
十、RequestVote 的日志新旧判断
接收方通常在以下条件下投票:
- 候选 Term 不小于本地。
- 当前 Term 尚未投票,或已经投给同一候选。
- 候选日志至少和本地日志一样新。
日志新旧先比较最后一条日志的 Term,Term 大者更新;Term 相同再比较最后 index。
这个条件用于保证包含更多已提交信息的节点更有机会成为 Leader。不是简单比较“谁的日志条数更多”;较短但最后 Term 更高的日志可能更新。
十一、AppendEntries 同时承担心跳和复制
Leader 发送 AppendEntries,核心字段可抽象为:
term
leaderId
prevLogIndex
prevLogTerm
entries[]
leaderCommitFollower 检查 prevLogIndex 位置是否存在且 Term 等于 prevLogTerm。若不匹配,拒绝并让 Leader 回退 nextIndex;匹配后删除该位置之后冲突日志,追加新条目,再根据 leaderCommit 推进本地 commitIndex。
flowchart TD
A["Leader 发送 prevLogIndex、prevLogTerm 和 entries"] --> B{"Follower 前置日志是否匹配"}
B -- "否" --> C["返回失败和冲突提示"]
C --> D["Leader 回退 nextIndex 后重试"]
B -- "是" --> E["删除同位置后的冲突条目"]
E --> F["追加 Leader 日志"]
F --> G["更新 matchIndex 和 commitIndex"]这就是旧 Leader 少数派期间追加但未提交的日志最终可能被新 Leader 覆盖的原因。未提交不代表永远保留。
十二、Raft 何时算提交
Leader 收到多数节点已持久化某日志位置的确认后,才可能推进 commitIndex。Raft 对“通过计数提交旧任期日志”有重要限制:Leader 通常只通过多数复制直接提交当前任期条目;一旦当前任期条目提交,之前前缀日志也随之确定。
flowchart TD
A["Leader 本地追加 index=N, term=T"] --> B["并行复制给 Followers"]
B --> C["Followers 持久化后 ACK"]
C --> D{"多数 matchIndex 大于等于 N"}
D -- "否" --> E["条目保持未提交"]
D -- "是且日志属于当前Term" --> F["Leader 推进 commitIndex"]
F --> G["应用状态机并回复客户端"]
G --> H["后续心跳把 leaderCommit 告知 Followers"]“Leader 本地写盘成功”不等于集群提交;“复制到一个 Follower”在 5 节点集群也不等于提交;客户端应以协议提交结果为准。
十三、客户端超时仍然是不确定状态
可能发生:
客户端发送 commandId=R100
→ Leader 复制到多数并提交
→ Leader 在返回前宕机或响应丢失
→ 客户端超时客户端不能换一个新业务 ID 再执行,否则可能重复。应使用稳定 commandId/幂等键,重试后由状态机返回同一结果,或先查询提交状态。共识保证日志一致,不自动提供业务“恰好一次”。
十四、读请求为什么也有一致性问题
直接读任意 Follower 可能读到旧数据。常见读语义:
| 读方式 | 一致性 | 代价 |
|---|---|---|
| 本地 Follower 读 | 可能陈旧 | 延迟低 |
| Leader 租约读 | 条件成立时可线性一致 | 依赖时钟和租约假设 |
| ReadIndex/多数确认 | 线性一致性更稳妥 | 多一次确认或等待应用进度 |
| 写后携带版本读 | 可实现读己之写 | 客户端和服务端管理版本 |
Leader 也可能已经失去多数派却尚未意识到。线性一致读不能只判断本地角色是 Leader,还要确认自己仍有领导权并且状态机已经应用到安全读位置。
十五、日志压缩与快照
日志无限增长会增加磁盘、重启回放和新节点同步成本。系统会在某个已应用 index 创建状态机快照,并截断更早日志。
落后 Follower 若需要的日志已被 Leader 压缩,Leader 通过 InstallSnapshot 发送快照,再继续复制后续日志。
快照必须与 lastIncludedIndex、lastIncludedTerm 对齐,并保证写入原子性;损坏快照或截断未提交日志会破坏恢复。
十六、成员变更为什么危险
若直接从旧配置 {A,B,C} 切到新配置 {C,D,E},旧多数 {A,B} 和新多数 {D,E} 可能完全不相交,产生两个都自认合法的决策组。
Raft 常使用联合共识或等价的分阶段成员变更:过渡阶段同时满足旧配置和新配置的多数要求,再切换到新配置。实际产品可能限制一次只增删一个成员以简化安全实现。
扩容不是把配置文件加一个 IP;要考虑新节点追日志、快照、投票资格、流量和磁盘容量。
十七、Basic Paxos 核心角色
| 角色 | 作用 |
|---|---|
| Proposer | 提出带编号的提案和值 |
| Acceptor | 对提案作 Promise 和 Accept,持久化承诺 |
| Learner | 学习最终被多数接受的值 |
一个节点可以同时承担多个逻辑角色。Basic Paxos 解决一个槽位选一个值;实际复制日志需要 Multi-Paxos 等扩展对连续槽位反复达成共识。
十八、Paxos 两阶段消息
Phase 1:Prepare / Promise
Proposer 选择全局递增、唯一的 proposal number n,向多数 Acceptor 发送 Prepare(n)。Acceptor 若尚未承诺更高编号,则承诺不再接受编号小于 n 的提案,并返回自己已接受的最高编号提案和值。
Phase 2:Accept Request / Accepted
Proposer 收到多数 Promise 后:若响应中已有被接受值,必须选择最高 accepted proposal 对应的值;否则可使用自己的值。然后发送 Accept(n, value)。Acceptor 若未承诺更高编号,就接受并持久化。
flowchart TD
A["Proposer 发送 Prepare(n)"] --> B["多数 Acceptor 返回 Promise"]
B --> C["选择响应中最高已接受提案的值"]
C --> D["发送 Accept(n, value)"]
D --> E{"多数 Acceptor 接受"}
E -- "是" --> F["该槽位的值被选定"]
E -- "否" --> G["使用更高编号重新开始"]“必须沿用最高编号已接受值”是安全核心之一,它让新提案继承可能已被选定的旧值,而不是覆盖它。
十九、Multi-Paxos 为什么引入稳定 Leader
若每个日志槽位都完整执行 Prepare 和 Accept,消息和冲突成本很高。Multi-Paxos 在稳定 Leader 存在时,可在建立领导权后对后续槽位主要执行接受阶段,减少往返。
Paxos 与 Raft 都常以 Leader 提高工程效率,但概念和证明方式不同。不能简单说“Raft 就是 Paxos 换名字”,也不能因 Raft 更易理解就认为 Paxos 不安全。
二十、ZAB 为 ZooKeeper 解决什么
ZAB 是 ZooKeeper 的原子广播协议,围绕主备复制、Leader 恢复和事务顺序设计。核心目标包括:
- 所有已提交事务按相同全局顺序交付。
- 新 Leader 包含此前已提交历史。
- Leader 切换时同步 Follower,处理未提交提案。
- 广播阶段由 Leader 提议、Quorum ACK、再发送 COMMIT。
ZooKeeper 的 zxid 通常用于标识事务顺序,可理解为包含 Leader epoch 和该 epoch 内计数的 64 位标识。新 Leader 建立新 epoch,避免旧 Leader 提案混入新历史。
二十一、ZAB 的恢复与广播
flowchart TD
A["集群启动或 Leader 故障"] --> B["Leader Election"]
B --> C["Discovery:确定新 epoch 和历史"]
C --> D["Synchronization:DIFF、TRUNC 或 SNAP 对齐"]
D --> E["多数节点完成同步"]
E --> F["Broadcast:Leader 接收写请求"]
F --> G["生成 Proposal 与 zxid"]
G --> H["Followers 持久化并 ACK"]
H --> I{"收到 Quorum ACK"}
I -- "是" --> J["Leader 发送 COMMIT 并交付"]
I -- "否" --> K["不能确认提交"]Follower 落后时可能接收增量 DIFF;存在冲突的未提交尾部可能需要 TRUNC;差距过大时传输 SNAP。具体消息和实现随 ZooKeeper 版本演进,但“选主后先恢复一致历史,再开放广播”是核心。
ZAB 与 Raft 都有 Leader、epoch/term、日志和多数派,但 ZAB 是 ZooKeeper 语义下的原子广播协议,不能把其 RPC 和提交规则逐字段等同于 Raft。
二十二、Paxos、Raft、ZAB 对比
| 对比 | Paxos/Multi-Paxos | Raft | ZAB |
|---|---|---|---|
| 主要表达 | 提案编号、Acceptor 多数接受 | Term、Leader、复制日志 | Epoch、Leader、原子广播 |
| 工程理解 | 证明抽象、变体多 | 明确拆分选主、复制、安全 | 深度服务 ZooKeeper 语义 |
| 稳定期 | Multi-Paxos 常用稳定 Leader | Leader 复制 AppendEntries | Leader 广播 Proposal/Commit |
| 恢复重点 | 新提案继承已选值 | 新 Leader 日志完整性 | 新 Leader 历史同步与 zxid |
| 常见系统 | Chubby 思想、许多 Paxos 变体 | etcd、Consul、KRaft 等 Raft 系实现 | ZooKeeper |
具体产品可能采用改进版、库实现或额外工程协议,不应仅凭产品宣传词推断所有细节。
二十三、常见组件边界
| 组件 | 共识/复制定位 | 不应误解 |
|---|---|---|
| etcd | 使用 Raft 维护一致日志与 KV 状态 | Follower 任意本地读不天然线性一致 |
| Consul Server | 使用 Raft 管理目录和配置状态 | Agent 健康检查本身不是共识 |
| ZooKeeper | 使用 ZAB 复制事务和恢复历史 | Watcher 不是持久消息队列 |
| Kafka KRaft | 使用 Raft 派生协议管理集群元数据 | 普通 Topic 数据复制不是简单等同控制器元数据 Raft |
| Redis Sentinel | 监控、故障转移和 Leader 选举协作 | Redis 主从异步复制不因此变成强一致共识存储 |
| Redlock | 多独立 Redis 节点的多数加锁尝试 | 不是 Raft/Paxos 强共识;关键业务需 fencing token 和事实源约束 |
| Nacos | 不同一致性/实例模型使用不同协议实现 | 不能一概说整个 Nacos 永远是 CP 或 AP |
二十四、JDK 8 可运行多数派提交实验
下面是教学模拟,只演示“Leader 本地追加、Follower ACK、多数派提交、少数派不能提交”,不包含选举、持久化、日志回退、成员变更和完整 Raft 安全证明,不能用于生产。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class RaftQuorumDemo {
public static void main(String[] args) {
Cluster cluster = new Cluster(5);
cluster.setReachableFollowers(1, 2);
boolean first = cluster.append("SET version=7");
System.out.println("firstCommitted=" + first
+ ", commitIndex=" + cluster.commitIndex);
cluster.setReachableFollowers(1);
boolean second = cluster.append("SET version=8");
System.out.println("secondCommitted=" + second
+ ", commitIndex=" + cluster.commitIndex);
cluster.printLogs();
}
static class Cluster {
private final List<Node> nodes = new ArrayList<Node>();
private final Node leader;
private List<Integer> reachableFollowers =
new ArrayList<Integer>();
private int currentTerm = 3;
private int commitIndex = 0;
Cluster(int nodeCount) {
for (int i = 0; i < nodeCount; i++) {
nodes.add(new Node(i));
}
leader = nodes.get(0);
}
void setReachableFollowers(Integer... ids) {
reachableFollowers = Arrays.asList(ids);
}
boolean append(String command) {
int index = leader.log.size() + 1;
Entry entry = new Entry(index, currentTerm, command);
leader.log.add(entry);
int acknowledgements = 1;
for (Integer followerId : reachableFollowers) {
Node follower = nodes.get(followerId);
follower.appendCopy(entry);
acknowledgements++;
}
int quorum = nodes.size() / 2 + 1;
if (acknowledgements >= quorum) {
commitIndex = index;
markCommitted(index);
return true;
}
return false;
}
void markCommitted(int index) {
for (Node node : nodes) {
for (Entry entry : node.log) {
if (entry.index <= index) {
entry.committed = true;
}
}
}
}
void printLogs() {
for (Node node : nodes) {
System.out.println("node-" + node.id + " " + node.log);
}
}
}
static class Node {
private final int id;
private final List<Entry> log = new ArrayList<Entry>();
Node(int id) {
this.id = id;
}
void appendCopy(Entry source) {
log.add(new Entry(
source.index, source.term, source.command));
}
}
static class Entry {
private final int index;
private final int term;
private final String command;
private boolean committed;
Entry(int index, int term, String command) {
this.index = index;
this.term = term;
this.command = command;
}
@Override
public String toString() {
return "{" + index + ",t=" + term + ",cmd="
+ command + ",committed=" + committed + "}";
}
}
}5 节点多数派是 3。第一次 Leader 加两个 Follower 共 3 个 ACK,可以提交;第二次仅 Leader 加一个 Follower 共 2 个 ACK,日志可以局部存在但不能提交。后续新 Leader 可能覆盖未提交尾部。
实际运行的预期输出:
firstCommitted=true, commitIndex=1
secondCommitted=false, commitIndex=1
node-0 [{1,t=3,cmd=SET version=7,committed=true}, {2,t=3,cmd=SET version=8,committed=false}]
node-1 [{1,t=3,cmd=SET version=7,committed=true}, {2,t=3,cmd=SET version=8,committed=false}]
node-2 [{1,t=3,cmd=SET version=7,committed=true}]
node-3 []
node-4 []注意 node-2 没有第二条日志,node-3、node-4 连第一条也还没有,但第一条仍可提交,因为提交要求多数派而不是所有节点。落后节点恢复连接后需要通过日志复制或快照追平。第二条只存在于 Leader 和 node-1,不能应用到状态机,也不能向客户端承诺成功。
二十五、共识集群生产排查
25.1 无 Leader 或频繁选主
检查节点间网络延迟、丢包、GC 停顿、CPU、磁盘 fsync、选举超时、时钟告警、证书和成员地址。频繁换主不是简单“把超时调大”;要先证明是网络、磁盘还是资源停顿。
25.2 有 Leader 但写不进去
检查是否仍拥有多数派、Follower 复制延迟、磁盘满、日志持久化失败、成员配置、请求是否发到只读节点。Leader 名称存在不代表能获得 Quorum。
25.3 Follower 长期落后
检查网络带宽、快照传输、日志积压、磁盘吞吐、单条日志大小和状态机应用速度。不要让共识日志承载大块业务文件。
25.4 读到旧值
确认读 API 的一致性级别:是否允许串行一致/陈旧读,是否直接读 Follower,线性一致读是否需要 Leader/ReadIndex,多数据中心代理是否读了不同集群。
25.5 扩容后反而变慢
多数派增大、复制目标增多、新节点追日志和快照都会增加成本。3 扩 4 通常不提高故障容忍,3 扩 5 才从容忍 1 个提升到 2 个,但写确认和运维成本也增加。
二十六、常见误区
| 误区 | 正确理解 |
|---|---|
| 多数派就是任意过半机器在线 | 还要合法任期、日志新旧、持久化和成员配置 |
| Leader 本地写成功就是提交 | 必须满足协议的 Quorum 提交条件 |
| Raft 自动保证业务恰好一次 | 客户端超时仍要幂等 ID 和结果查询 |
| 任意 Follower 读都是最新 | 取决于读一致性模式 |
| 节点越多越可靠且越快 | 多数派和复制成本会增加 |
| Redlock 等同 Raft | Redlock 不是复制状态机共识协议 |
| ZAB 就是 Raft | 有相似结构,但协议目标、消息和恢复实现不同 |
| 共识能替代分布式事务 | 共识日志与跨业务资源事务是不同层次 |
二十七、面试标准回答
共识算法让多个非拜占庭节点在宕机、网络分区、消息延迟和乱序下,对日志顺序达成安全一致。多数派的核心是任意两个 Quorum 必有交集。Raft 使用 Term、Follower/Candidate/Leader、RequestVote 和 AppendEntries:候选日志必须足够新才能获票,Leader 通过 prevLogIndex/prevLogTerm 对齐日志,条目复制到多数并满足提交规则后推进 commitIndex。少数派旧 Leader 即使仍存活也无法提交。Paxos 通过 Prepare/Promise 和 Accept 两阶段,让新提案继承可能已经被选定的值;Multi-Paxos 使用稳定 Leader 降低连续日志槽位成本。ZAB 为 ZooKeeper 提供 Leader 恢复、历史同步和 Proposal/ACK/COMMIT 原子广播。它们解决复制状态机一致性,不自动解决业务幂等和跨服务事务。
更深的实现追问、场景题和标准回答见 共识算法与 Raft 独立面试题;持久化、冲突回退、线性读、快照和成员变更原理见 Raft 内部原理与生产治理。
二十八、关联知识点
- CAP、BASE 与一致性理论
- Raft内部原理与生产治理
- 共识算法与Raft独立面试题
- 数据一致性设计
- ZooKeeper ZAB
- ZooKeeper Watcher 与 Session
- ZooKeeper 锁与选主
- 分布式锁扩展点
- 分布式事务
- 微服务与 Spring Cloud 组件全景
本章小结
共识的核心不是“Leader 把数据复制给 Follower”一句话,而是多数派交集、任期/提案编号、日志新旧比较、冲突回退、提交规则、状态机应用和恢复共同保证安全性。生产系统还必须处理客户端超时幂等、线性一致读、快照、成员变更、磁盘与网络故障;少任何一项,都不能仅凭“用了 Raft/ZAB”宣称强一致。
