Raft算法

前置

请先看完 http://www.kailing.pub/raft/index.html 后继续查看文章

角色

  • leader
  • follower
  • candidate

每个节点同时只扮演一个角色,允许切换

Leader

领导者,接收客户端请求,使用日志条目复制数据到Follower

维护状态

  • term: 当前任期号
  • log[]: 日志条目数组,每个条目包含客户端指令和产生该条目的任期号
  • commitIndex: 已被集群多数节点复制且可以提交的日志最大索引(所有≤该索引的日志均可应用到状态机)
  • lastApplied: 已应用到状态机的日志最大索引(记录本地状态机执行到哪条日志)
  • nextIndex[]: 对每个 follower 维护一个值,表示下一次向该 follower 发送日志的起始索引
  • matchIndex[]: 对每个 follower 维护一个值,表示该 follower 已成功复制的最大日志索引

Follower

跟随者,被动接受Leader的日志复制请求和心跳包

维护状态

  • term: 当前任期号(与 leader 保持一致,或在发现更高任期时更新)
  • log[]: 日志条目数组(与 leader 同步的日志副本)
  • commitIndex: 已被集群多数节点复制且可以提交的日志最大索引(由 leader 通过心跳同步)
  • lastApplied: 已应用到状态机的日志最大索引(记录本地状态机执行到哪条日志)
  • votedFor: 当前任期内投票给的节点 ID(用于避免重复投票,保证每个任期仅投一票)
  • 超时计时器:用于检测 leader 是否失联(若超时未收到 leader 心跳,会转换为 candidate 发起选举)

Candidate

候选者,当Follower在一定时间没有接收到Leader心跳包后将从follower切换到candidate进行选举

请求

日志复制

作用: 专门用于向 follower 同步新的日志条目

内容: 包含 leader 的任期号(term)、前一条日志的索引和任期(用于 follower 校验匹配)、需要同步的日志条目(可能是一条或多条)、leader 当前的commitIndex(用于同步提交状态)

心跳包

作用:用于维持leader的地位,定时发送,防止follower因超时触发新的选举。

内容:格式与日志复制请求基本一致,但不包含任何新的日志条目(可以理解为空日志的复制请求),同样会携带 leader 的termcommitIndex

投票请求

作用: 竞选leader

内容: 等待150ms-300ms随机时间过期后为自身term+1,给自己投一票,然后向其他节点发送投票请求,多数派同意后变成leader

成员变更请求

作用: 动态增删节点,避免脑裂问题

执行流程

  1. 客户端向Leader发送请求
  2. Leader接受到请求,使用日志复制将数据同步到Follower
  3. 多数派Follower处理完成响应给Leader
  4. Leader收到多数派响应,修改自身CommitIndex,返回响应给客户端
  5. Leader等待下次心跳包进行同步提交信息

如果在同步提交信息前挂掉了怎么办?

此时Follower变成Candidate进行选举,注意,只有日志与多数派相同的Candidate才会被选举为Leader

  1. 新的Leader当选,任期+1,立刻触发日志同步步骤如下
  2. 向所有follower发送包含自身日志信息的同步请求(基于nextIndex初始化值)
  3. 当follower返回响应时,NewLeader会通过matchIndex记录每个follower已复制的最大日志索引
  4. 当NewLeader发现某条日志的matchIndex已满足多数派已复制(即超过半数 follower 的matchIndex≥该日志索引),就会直接将自己的commitIndex更新为该日志索引
  5. 此时之前Leader未同步提交的信息将会被标记提交

Reference

[]: http://www.kailing.pub/raft/index.html "Raft 算法动画演示"
[]: https://chatgpt.com/ "ChatGPT"
[]: https://www.doubao.com/ "豆包"

算法Raft一致性算法

我来吐槽

*