共识集群网络分区恢复后如何处理日志冲突?
简化版
网络分区期间只有获得多数派的 Leader 能提交日志,少数派上的旧 Leader 即使暂时接受请求,也不能把未提交条目视为成功。 恢复后,节点比较任期与日志前缀;旧 Leader 发现更高任期后退为 Follower,当前 Leader 通过 prevLogIndex/prevLogTerm 找到共同前缀,截断冲突的未提交后缀并复制权威日志。 已提交日志绝不能被覆盖,这是共识安全性的底线。
详细版
Raft 的日志匹配属性保证:若两条日志在同一 index、term 相同,则此前条目相同。 Leader 为每个 Follower 维护 nextIndex/matchIndex,AppendEntries 校验前一条日志;不匹配就回退或利用冲突提示快速定位。 匹配后 Follower 删除冲突后缀,追加 Leader 条目。
客户端只有在条目被当前规则提交后才收到成功。 分区中超时的请求状态不确定,重试必须带幂等键,因为原请求可能已经提交。 恢复观测要区分日志追平、状态机应用追平与业务可服务,安装快照也要验证 lastIncludedIndex/Term。
完整版教学
一、分区期间谁能继续提交
在 5 节点集群中,3 节点分区可形成多数派,2 节点分区不能。 旧 Leader 若落在少数派,可能一段时间仍自认为 Leader,但无法获得多数确认。 它必须让写请求超时或失败,不能把本地追加等同于提交成功。
冲突修复可以删除未提交日志,但任何已提交日志都必须在后续 Leader 中保留。
二、任期先解决领导权
多数派可能选出更高 term 的新 Leader。 网络恢复后,旧 Leader 在 RPC 中看到更高 term,立即更新任期并退为 Follower。 低任期节点不能继续发送成功响应或覆盖高任期日志。 选举限制要求候选人的日志至少与投票者一样新,帮助保留已提交条目。
三、如何找到共同前缀
AppendEntries 携带 prevLogIndex 与 prevLogTerm。 Follower 只有本地对应位置任期相同才接受后续条目。
Leader: 1/1 2/1 3/2 4/4 5/4
Follower: 1/1 2/1 3/2 4/3 5/3
common prefix ends at index 3
Leader 逐步回退 nextIndex,或根据 Follower 返回的冲突任期快速跳过一段。
四、截断哪些日志
| 日志状态 | 分区恢复后的处理 | 原因 |
|---|---|---|
| 已提交且已应用 | 必须保留 | 已经对客户端产生确定结果 |
| 已提交但未应用 | 补齐后继续应用 | 共识结果已确定,只是状态机落后 |
| 旧 Leader 的未提交尾部 | 可被新 Leader 覆盖 | 尚未形成多数派决定 |
| 落后 Follower 缺少的已提交前缀 | 通过日志或快照补齐 | 合法新 Leader 已包含所有已提交历史 |
找到共同前缀后,Follower 删除与 Leader 冲突的后缀,再追加 Leader 的条目。 示例中的 4/3、5/3 若未提交,可以被 4/4、5/4 替换。 状态机只能应用已提交条目,因此被截断的未提交命令不应已经产生不可逆业务副作用。 实现若在提交前执行外部调用,就破坏了复制状态机假设。
五、已提交条目为何不会丢
一条日志提交后已存在于多数派。 任何新 Leader 的选举也需要多数票,两组多数派至少相交一个节点。 再配合“日志不够新不投票”的规则,新 Leader 必须包含已提交历史。 Raft 对当前任期条目的提交规则还会间接提交此前任期条目,不能只凭旧任期副本计数贸然推进。
六、落后太多时安装快照
Follower 的 nextIndex 若早于 Leader 已压缩日志起点,无法靠 AppendEntries 补齐。 Leader 发送快照,包含 lastIncludedIndex、lastIncludedTerm 和状态机数据。 Follower 校验并原子安装,再从快照后的日志继续追赶。 快照传输中断应可续传或安全重试,不能暴露半安装状态。
七、客户端和监控边界
客户端超时不代表写未提交,重试需要 requestId 和结果查询。 监控 term 变化、角色切换、冲突回退次数、nextIndex 差距、快照安装、commit-applied gap。 Follower 日志追到 commitIndex 后,状态机可能仍在应用,不能立刻用于要求新鲜度的读。 恢复演练应注入双向断网、单向丢包和长时间暂停。
八、常见误区与追问
- 误区:网络恢复后合并两边日志。 共识日志不是 Git,权威 Leader 覆盖冲突的未提交后缀。
- 误区:旧 Leader 接受的所有写都必须保留。 未获多数提交的条目可以被截断。
- 误区:Follower 冲突时清空全部日志。 应保留共同前缀,仅修复冲突后缀。
- 误区:日志追平就代表状态机追平。 appliedIndex 仍可能落后 commitIndex。
- 追问:客户端超时后能否直接重试? 可以,但必须幂等并允许查询原请求结果。
- 追问:为什么提交日志不会被覆盖? 多数派交集、投票新旧判断和 Leader 完备性共同保证。
- 追问:缺失日志已被压缩怎么办? 安装带 index/term 的快照后继续复制。
九、加强记忆
- 少数派不能提交写。
- 更高任期让旧 Leader 退位。
- prevLogIndex/Term 找共同前缀。
- 只截断冲突且未提交的后缀。
- 太落后则安装快照,客户端超时请求必须幂等处理。