《深入理解 Nacos 集群与 Raft 协议》系列四:日志复制机制:Raft 如何确保提交可靠且幂等

《深入理解 Nacos 集群与 Raft 协议》系列

大家好,我是G探险者!

在前几篇中我们介绍了选主与日志对比机制,它们保证了"谁能成为 Leader"以及"Leader 的日志是否可靠"。

而当 Leader 已选定,系统需要把客户端的写请求写入所有节点的日志中,这个过程就称为 日志复制(Log Replication)

本篇将讲解:

  • Raft 是如何进行日志复制的?
  • 如何判断日志是否成功提交?
  • 如何保障幂等与一致性?

一、基本流程:写入 Leader,然后复制给 Follower

在 Raft 中,所有写操作必须提交到 Leader 节点,流程如下:

  1. 客户端发送写请求给 Leader
  2. Leader 将写请求封装为日志条目(LogEntry)
  3. Leader 将该日志追加到本地日志中
  4. 并并发向所有 Follower 发送 AppendEntries 请求
  5. Follower 收到后尝试匹配前一条日志(前向一致性)
  6. 匹配成功后,Follower 追加日志,并回复成功
  7. Leader 收到超过半数节点确认后 → 标记日志为"已提交"
  8. Leader 通知 Follower 提交该日志(commit)
  9. Leader 应用日志到状态机并响应客户端

整个过程看似复杂,但每一步都有其必要性。


二、什么是日志"提交"成功?

Raft 中,一条日志被称为"已提交",必须满足:

该日志由 Leader 追加,且已被超过半数节点确认持久化。

这样做的意义是:

  • 即使 Leader 崩溃,仍有过半节点持有该日志
  • 下次选主,一定会选出有这条日志的节点为新 Leader(见第 3 篇)

已提交的日志,不会丢。


三、AppendEntries 的核心要素

AppendEntries 请求中包含:

  • leaderTerm:当前任期
  • prevLogIndex + prevLogTerm:前一日志索引+任期(对齐用)
  • entries:本次追加的日志条目(可能为空,仅用于心跳)
  • leaderCommit:Leader 当前提交的位置

Follower 会进行对比:

  • 若 prevLogIndex 和 prevLogTerm 不一致 → 拒绝请求
  • 否则 → 覆盖旧日志,从当前追加新日志

四、如何保证日志幂等?

在网络不稳定情况下,可能出现 AppendEntries 重发、乱序。

Raft 通过"前一条日志"对齐机制来保证幂等性:

  • 每次发送都携带 prevLogIndex/prevLogTerm
  • 若日志有误,Follower 会拒绝,Leader 自动回退并重发
  • 不会重复插入相同日志,也不会错位插入

→ 这是一种 乐观+校验补偿式的强一致写入机制


五、提交顺序与状态机执行

Raft 要求日志是顺序提交的:

  • 日志 index 是严格递增的
  • 必须 index=1 提交后才能提交 index=2

Leader 提交一条日志后:

  • 会逐步推进 commitIndex
  • Leader 会把 commitIndex 推送给所有 Follower
  • 每个 Follower 在收到时才会"真正执行"该日志到状态机

→ 所有节点最终状态机执行的日志是一致的、顺序的


六、断电/重启后如何恢复日志?

因为日志会持久化在本地磁盘上,所以:

  • Raft 节点宕机重启时,会从本地恢复日志
  • 并向新 Leader 进行日志对齐补偿

Raft 的一致性依赖于 持久化日志+对比同步机制,而非内存


七、日志复制在 Nacos 中的体现

你可以看到类似日志:

text 复制代码
[RAFT] AppendEntries from leader, prevIndex=5, term=7
[RAFT] Log mismatch, conflict at index=5, localTerm=6
[RAFT] Truncate logs from index=5
[RAFT] Append new log entries...
[RAFT] Advance commitIndex to 8

这些就是日志对齐 + 复制 + 提交的全过程。


总结

Raft 的日志复制机制,是系统强一致性的核心支柱:

  • 所有写请求必须经 Leader 统一调度
  • 复制必须超过半数成功才算提交
  • 提交后才能执行,确保所有节点状态一致
  • Follower 校验 + 回退机制保证了幂等性

下一篇,我们将以 Nacos 为例讲解:

💡 如果集群未过半节点存活,为什么整个系统不可用?

敬请期待第 5 篇!

相关推荐
优秀的颜31 分钟前
计算机基础知识(第五篇)
java·开发语言·分布式
coderSong25685 小时前
Java高级 |【实验八】springboot 使用Websocket
java·spring boot·后端·websocket
Mr_Air_Boy6 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
咖啡啡不加糖7 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
大鸡腿同学7 小时前
纳瓦尔宝典
后端
棠十一8 小时前
Rabbitmq
分布式·docker·rabbitmq
Lansonli8 小时前
大数据Spark(六十一):Spark基于Standalone提交任务流程
大数据·分布式·spark
2302_809798329 小时前
【JavaWeb】Docker项目部署
java·运维·后端·青少年编程·docker·容器
zhojiew9 小时前
关于akka官方quickstart示例程序(scala)的记录
后端·scala