Raft系列1 基本概念(原创)

可能是你能找到的最完整最详细的中文版的Raft算法说明博客

内容来源都是原生的论文,可以保证内容的可靠性,并且对论文里面的很多细节做了扩展说明

章节介绍

Raft系列1 基本概念(原创)
https://www.jianshu.com/p/0e695b4be92f
Raft系列2 核心概念(角色&Rules)
https://www.jianshu.com/p/00e89b5e9669
Raft系列3 总体心得
https://www.jianshu.com/p/496981a62bbe
Raft系列4 任期和选举
https://www.jianshu.com/p/aa137c94ac0b
Raft系列5 客户端设计
https://www.jianshu.com/p/bb7388b0cde6
Raft系列6 AppendEntries RPC
https://www.jianshu.com/p/d3247e2203a6
Raft系列7 快照技术和动态修改配置
https://www.jianshu.com/p/12e8bc4f384c
Raft系列8 一致性分析
https://www.jianshu.com/p/6344f202b7e5

Raft集群的模型介绍

image

图形来看有2个内容:request的完整流程走向、一个Node的主要构成

  1. 数据流程的走向:
  • 客户端发起一个命令,比如设置 x->3,过程1
  • 会通过Raft集群的一致性模型的管理,最终request会传达到leader节点
  • leader会把这个操作步骤写在自己本地日志,并且会通过一致性模型,通知到其它的各个follower节点,过程2
  • 超过半数follower复制反馈成功,leader再把这个操作应用到状态机中,可能以前状态机中没有x的值,或者x的值为2,最终都会在状态机中生成,x->3,过程3
  • 最后再返回给客户端,操作成功,过程4
  1. 一个Node的主要构成: 一致性模型、日志、状态机

log[](command)

image
  1. log除了有index的概念(下标从1开始),还有term的概念(这个log是在哪个term添加的),另外log本身就是一个命令 command,比如 x->3

  2. raft集群中的log指的是一个命令,或者说是一个操作而不是一个value,比如 x 设置为 3,或者 y 设置为 5.

  3. log[]中有的命令数组,不一定是最终会执行的命令。只有半数通过,最终leader commit了,才会apply到状态机中。

  4. 一个log[]完成不了一致性的功能,还依赖Node的commitIndex(半数通过了的命令),lastApplied(最终运用到状态机当中的命令)下标,最终实现一致性

  5. 关于log的操作有3种:copy、commit、apply。 copy是leader把新日志发送给所有follower的过程,commit是leader统计到超过半数的节点完成了复制,然后更新自己的commitIndex的过程,apply是完成更新commitIndex时,节点把最新commit的命令,按照顺序执行到自己的状态机里面的一个过程

状态机state Machine

  • 每个节点都有一个状态机,一个Raft集群就是一组状态机,zookeeper也是通过这种方式实现一致性管理的(复制状态机)
  • 注意状态机和日志的区别,日志存储的是数据操作的操作日志,状态机存储的是数据操作的结果值。
    比如客户端发起3个操作,x:1, x:2, x:3,则有3个操作日志,但是存储在状态机中只有1个键值对: x:3。

状态机的作用:

  • 存储的数据是这个值的最终态,而不是过程态(比如 x:3),可以提升查询的效率,直接返回结果
  • 存储半数节点都通过的数据(x:3是半数节点一致通过的结果,而log里面的命令,不一定会被执行,可能因为leader的失败,而导致这个命令删除或者被覆盖掉)

日志的作用:管理和同步数据用的。日志里面的数据(command)可能会被覆盖。

Server的详细组成

image

每个状态机因为role不同,会存储不同的内容
会持久化的内容: (每次响应RPC的时候都会update值到硬盘上)

  • currentTerm
  • votedFor
  • log[]: log有index,term,command等概念

不会持久化的内容:

  1. 每个节点都有一个状态机,来存储所有command按照日志顺序结构执行的最终结果

  2. 所有临时存储的内容:commitIndex、lastApplied(节点收到leader的最新的commitIndex,更新完自己的commitIndex之后,开始把最新的log里面的command apply到状态机中,操作结束后更新lastApplied的值),所以理论上commitIndex的值和lastApplied的值大部分的时候是相等的,或者讲当server发现lastApplied

  3. Leader节点存储的临时的内容(每次重新选举会初始化):

  • nextIndex[] :下一个待发送日志的下标,初始化值是 新leader的last Log Index +1
  • matchIndex[]:日志最匹配的一个下标,初始化值是0

非常巧妙的一个设计,理论上正常情况下,matchIndex=nextIndex-1
比如leader的日志条目数是10个且最新没有要添加的日志,其中4个follower,数据都是同步的,那么nextIndex[] : 11,11,11,11 。matchIndex[] 为 10,10,10,10
如果当前leader有一个新的日志要添加,那么AppendEntries RPC到各个follower,response成功后,更新nextIndex[] 和matchIndex[]

主要的发挥用途是在状态异常的时候

  • 老leader挂了,新leader当选,默认nextIndex为11,matchIndex为0,发送一次同步之后,如果数据都一样,那么更新为 nextIndex为11,matchIndex为10。
    如果数据有gap,AppendEntries RPC response false,那么nextIndex会递减1,leader发送的log为 log[10],如果还失败 继续递减,直到返回true,再挨个的同步数据
  1. 一个计时器(计算心跳超时和 选举超时)
  2. 一个统计器(leader会统计成功复制的节点个数是否超过半数,candidate会统计获得的投票数是否超过半数)

注意事项:

  1. leader为了提升性能,会并发性的给所有follower发消息,但是每个RPC的内容可能是不同的,是根据nextIndex[]来发的,如果所有的follower的状态都一致,那么nextIndex[]的值都一样
  2. 感觉matchIndex[]和nextIndex[]有冗余,因为matchIndex[]的index+1就是nextIndex的值

RPC

  1. Raft中的RPC分为2种大的类型:选举消息和状态同步类消息。按照数据结构来分为3种RPC:RequestVote RPC、AppendEntries RPC、InstallSnapshot RPC。按照功能分为5种RPC(选举、同步log、心跳、新leader的宣誓主权的消息、同步快照)。
  2. 日志同步类消息分为:AppendEntries RPC和 InstallSnapshot RPC(快照方式同步数据)
  3. AppendEntries RPC按照功能来分:heartbeat功能的消息、同步log的消息、宣誓主权的同步内容为空的log消息(可以确保老leader遗留给新leader的消息仍然会被执行)
  4. Raft集群间的RPC消息是双向的,就是消息发起人发起一个消息,消息接收人会返回一个结果,这才构成一个完整的消息
  5. 如果 follower 崩溃或者运行缓慢,或者网络丢包,leader 会不断地重试 AppendEntries RPC(即使已经回复了客户端)直到所有的 follower 最终都存储了所有的日志条目
  6. Raft 的 RPCs不仅交互失败会发起重试,而且这种重试都是幂等的,所以这样的重试不会造成任何伤害。例如,一个 follower 如果收到 AppendEntries 请求但是它的日志中已经包含了这些日志条目,它就会直接忽略这个新的请求中的这些日志条目。

参考

主要参考Stanford University的原生论文以及中文的翻译版本

Raft算法中文版(论文翻译)
https://www.cnblogs.com/linbingdong/p/6442673.html

如果你觉得对你有帮助的话,一定要点赞!

你可能感兴趣的:(Raft系列1 基本概念(原创))