R

[Raft] Raft日志

RoLingG 其他 2025-11-14

Raft日志

取自:https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/lecture-06-raft1/6.5-ri-zhi-raft-log

Raft 日志对于 Leader(主节点)Follower(副本节点)所有节点,都有对应的重要功能:

  • 对于 Leader 来说,Log 是 Leader(主节点) 用来对操作排序的一种手段。我们都知道 Follower 要根据 Follower 的操作进行同步,为了实现多个 Follower 都能精准同步主节点,Leader 必须对这些请求确定一个顺序,并确保所有其他的 Follower 都遵从这个顺序。实际上,Log 是一些按照数字编号的槽位(类似一个数组),槽位的数字表示了 Leader 操作选择的顺序。

    Leader Log 是一条条按索引顺序排列的写操作记录,Leader 先把它持久化到本地磁盘,再复制给 Follower;只要多数派落地,就标记为 committed。
  • 对于 Leader 来说,Log 的另一个功能是记录操作,因为这些操作可能需要重传给 Follower。如果一些 Follower 由于网络原因或者其他原因短时间离线了或者丢了一些消息,Leader 需要能够向 Follower 重传丢失的 Log 消息。包括那些已经 commit 的请求,为了能够向丢失了相应操作的副本重传,也需要存储在 Leader 的 Log 中。
  • 对于 Follower 来说,在 Follower 收到了操作,但是还没有执行操作时,肯定需要一个暂存的地方,这个地方就是 Log。Log能够帮助 Follower 记住 Leader 发送过来的顺序操作,直到收到了 Leader 发送的新的commit 号才执行。

    因为是暂存的,所以 Log 里记录的操作有可能会被舍弃掉。

    但记住,Follower 收到日志条目后必须持久化(落盘),然后才给 Leader 回 ACK。

    也就是说条目已经完好地保存在磁盘上,但整个集群还没有提交(commit)它,所以 Follower 还不能把它应用到状态机——这段“等待提交”的时期就被称为 暂存(pending)状态

  • 对于所有节点来说,Log 是能够帮故障节点重启恢复回可服务状态(也就是故障后重新加回 Raft 集群)。比如对于一个 3 节点的集群来说,如果一个节点故障重启之后不能自动加入,那么当前系统只剩 2 个节点,那将不能再承受任何故障,所以我们需要能够重新并入故障重启了的服务器。对于一个重启的服务器来说,会使用存储在磁盘中的 Log。每个 Raft 节点都需要将 Log 写入到它的磁盘中,这样它故障重启之后,Log 还能保留。而这个 Log 会被 Raft 节点用来从头执行其中的操作进而重建故障前的状态,并继续以这个状态运行。所以,Log 也会被用来持久化存储操作,服务器可以依赖这些操作来恢复状态

    说白了就是故障后根据 Log 批量顺序执行操作,恢复回故障前状态,从而能够返回集群中。

其实在技术栈中都有类似的设计思路,例如 Redis 的 AOF、MySQL 的 binlog 等,只不过粒度不同。

维度Raft-logRedis AOFMySQL binlog
记录内容共识后的写操作(已排序、已投票)客户端发来的原始命令已经引擎层执行完的变更事件(行/语句)
顺序保证集群级、线性一致(算法强制)单机顺序写文件,无分布式共识单机顺序写文件,主从异步复制
是否可丢弃/重写绝不允许丢弃已提交条目;未提交的可覆盖可重写(BGREWRITEAOF)、可丢失最后几秒可purge、可rotate,主从 Gap 时可补
重放目的1. 崩溃恢复 2. 集群共识(让Follower状态与Leader一致)节点本地崩溃恢复1. 本地恢复 2. 主从复制(仍可能延迟、不一致)
一致性级别线性一致(只要集群多数派存活就保证不丢)最终一致(AOF 仅本地,主从异步)最终一致(半同步/异步复制,存在幻读、回滚)

Follower 故障恢复

学生提问:如果其中一个服务器故障了,它的磁盘中会存有Log,因为这是Raft论文中图2要求的,所以服务器可以从磁盘中的Log恢复状态,但是这个服务器不知道它当前在Log中的执行位置。同时,当它第一次启动时,它也不知道那些Log被commit了。

Robert教授:所以,对于第一个问题的答案是,一个服务器故障重启之后,它会立即读取Log,但是接下来它不会根据Log做任何操作,因为它不知道当前的Raft系统对Log提交到了哪一步,或许有1000条未提交的Log。

如问中学生提问,这里教授并没有回答 Raft 是如何让故障重启后的 Follower 追上 Leader。关于这里GPT的回答是:

重启节点的“失忆”状态

  1. 磁盘里只有「原始日志 + 上一次的 currentTerm、votedFor」
  2. 完全不知道:

    • 自己上次 apply 到哪个 index(lastApplied)
    • 哪些日志已被提交(commitIndex)
    • 现在谁是 Leader、term 是多少

因此它绝不能擅自重放日志;必须先「重新加入集群」拿到权威信息,再决定该 apply 多少、该删改多少。完整流程如下:

一、启动后只做两件事

  1. 把持久化的日志载入内存(index/term 序列仍完整)。
  2. 把 currentTerm 强制加 1(避免旧 Leader 骚扰),状态设为 Follower,然后开始 election timeout 倒计时

二、马上就能知道“谁才是现任 Leader”

  • 只要收到任意 AppendEntries(或心跳),里面的 term ≥ 自己的 currentTerm,即可:

    • 更新 currentTerm = 对方 term
    • 认出对方是 Leader
    • 重置 election timer,正式进入跟随模式

三、立即就能知道“该 apply 到哪里” Leader 的每条 AppendEntries 都携带:

  • prevLogIndex / prevLogTerm(用于日志对齐)
  • leaderCommit(当前已提交位置)

Follower 用 leaderCommit 直接把本地的 commitIndex 设为 min(leaderCommit, 最后一个新条目下标),然后从 lastApplied+1 到 commitIndex 顺序 apply即可。
→ 因此不需要自己记住旧 commitIndex;Leader 会不断推给它最新值。

四、日志不一致怎么办 —— Leader 强制覆盖

  • prevLog 对不上,Follower 返回 false
  • Leader 递减 nextIndex 重试,直到找到共同前缀;Follower 把冲突后缀全部截断(磁盘也删),然后复制 Leader 的新条目
  • 最终保证:Follower 日志是 Leader 日志的前缀,且已落盘

五、快照场景(可选但常见) 如果落后太多,Leader 直接发送 InstallSnapshot RPC:

  • 快照里带 lastIncludedIndex/lastIncludedTerm
  • Follower 收到后先清空旧日志,加载快照,再记录下快照之后的少量新日志
  • 重启节点借此秒级追上,而不用逐条重放万条记录。

一句话总结

重启节点不需要自带“执行位置”或“提交位”;

它只要:

  1. 把磁盘日志原样读进来;
  2. 等第一个 AppendEntries 就认出 Leader 和最新 commitIndex;
  3. 按 Leader 的 commitIndex apply 到状态机;
  4. 如有冲突,让 Leader 用覆盖/截断帮它修好。

通俗点讲:

重启节点“傻”等着就行,Leader 自会把它拉到最新状态。

落后几百条 → Leader 一条条发 AppendEntries, follower 按顺序补日志、apply 到内存。

落后几千/上万条 → Leader 直接甩一个 InstallSnapshot, follower 先清空旧账,加载快照,再补少量新日志,秒级追上。

Leader 故障恢复

截取教授精简讲的话:

如果大家都出现了故障然后大家都重启了,它们中没有一个在刚启动的时候就知道它们在故障前执行到了哪一步。所以这个时候,会先进行 Leader 选举,其中一个被选为 Leader。

Leader 会在发送第一次心跳时弄清楚,整个系统中目前执行到了哪一步。Leader 会确认一个过半服务器认可的最近的 Log 执行点,这就是整个系统的执行位置。另一种方式来看这个问题,一旦你通过 AppendEntries 选择了一个 Leader,这个 Leader 会迫使其他所有 Follower 的 Log 与自己保持一致。

由于 Leader 知道它迫使其他所有的 Follower 都拥有与自己一样的 Log,那么它知道,这些 Log 必然已经 commit,因为它们被过半的副本持有。Leader 会增加 commit 号。之后,所有节点可以从头开始执行整个 Log,并从头构造自己的状态。但是这里的计算量或许会非常大。所以这种从头开始执行的机制不是很好,教授后面也介绍了 checkpoint 机制。

PREV
[Raft] Raft分布式强一致性方案

评论(0)

发布评论