我们知道了Raft可以实现一系列的值的共识之外,还能实现各个节点的日志的一致,这个日志是和业务数据紧密相关的

在Raft算法中,副本数据是以日志的方式存在的,领导者接收到来自客户端的写请求的时候,处理写请求,就是一个复制日志到状态机的过程

那么如何复制日志到其他的机器,日志的一致性如何保证的,就是重点问题,我们了解下复制日志,实现日志一致的基础

副本数据由日志形式存在的,日志由日志项组成,日志项是一种数据格式,主要包含用户指定的数据,就是所谓的指令Command,包含一些附加信息,比如索引值Log index,任期编号 Term

图片

指令是一条由客户端指定的,状态机需要执行的指令,可以将指令理解为客户端指定的数据

索引值,日志项对应的整数索引值,就是用来标识日志项的,一个连续的,单调递增的整数好吗

任期编号,创建这条日志项的领导者的任期编号

那么一届内的日志,往往是多条的,而日志的索引值是和一直持续连续的

而上面的图,暴露了一个问题,虽然Raft保证了节点之间的日志的一致性,但是每个跟随者之间的日志可能不一样,这就和日志复制的机制有关

我们可以将Raft日志复制理解为一个优化后的二阶段提交,减少了一半的往返提交,降低了一般的消息延迟

在整个的执行流程中,领导者进入了第一阶段,将日志通过RPC,复制到了集群其他节点上

然后领导者收到了大多数复制成功的响应后,将日志项应用到了自己的状态机,并且返回成功给客户端,如果领导者没有收到大多数的复制成功的响应,就返回错误给客户端

在上面的日志复制流程中,领导者不直接发送消息通知其他节点应用指定日志项,只是发给你了,用不用哪个随你,这样就不用等待应用了,降低了消息的延迟,具体的流程图如下

图片

1.接收到客户端请求后,领导者基于客户端请求中的指令,创建一个新的日志项,附加到本地日志中

2.领导者通过日志复制RPC,并将新的日志项复制到其他的服务器上

3.当领导者将日志项,成功的复制到大多数的服务器上的时候,就将这个日志项应用到自己的状态机上

4.然后根据这个返回执行结果

5.当跟随者接收到心跳信息,或者将新的日志复制RPC消息后,如果发现领导者已经提交了某条日志项,还没应用,就应用到自己的状态机里面

这是一个理想状态下的日志复制过程,在实际环境中,复制日志的时候,可能会进程崩溃,服务器宕机的问题,会导致日志不一致,Raft是如何处理不一致的日志问题的呢?

那么在Raft之中,如何保证节点间的日志一致性的呢?

首先,领导者通过日志复制RPC的一致性检查,找到跟随者节点上,和自己相同日志项的最大索引值,然后判断索引值是否有缺失

然后领导者强制跟随者更新覆盖不一致日志项,实现日志的一致

我们仔细的说一下这个过程

PrevLogEntry:当前复制的日志项的前面一条日志项的索引值,如果领导者将索引值为8的日志项发给追随者,PrevLogEntry值为7

PrevLogTerm:当时复制的日志项的前面一条日志项的任期编号,如果领导者将索引值为8的日志项发过去,那么PrevLogTerm值为4

图片

那么整个复制流程如下

1.;领导者通过日志复制RPC消息,发送最新的日志项到追随者,如果最新的消息为PrevLogEntry值为7,PervLogTerm值为4

2.如果跟随者在日志中,找不到PrevLogEntry值为7,PrevLogTerm值为4的项,就拒绝接收最新的日志项,并返回失败

3.这时候,领导者会递减复制的日志项索引值,并且发送新的日志项到跟随者

4.如果找到了PrevLogEntry值为6,PrevLogTerm值为3的日志项,就返回复制成功,领导者就知道了PrevLogEntry值为6的位置是跟随者的

5.然后通过日志复制RPC,进行之后的日志项的复制,实现了集群各个节点日志的一致

我们利用了日志RPC的一致性的检查,找到了最大能够同步的索引值,然后复制更新覆盖之后的日志项,实现了各个节点日志的一致性,需要注意的是,跟随者中不一致的日志项会被领导者的日志覆盖,并且领导者从不覆盖或者删除自己的日志

课后小结

我们认识到了Raft中什么是日志,如何复制这个日志,如何处理日志不一致的问题

Raft中,副本数据是以日志形式存在的,日志项中指令表示用户指定的数据

Raft中日志必须是连续的,因为是数据的载体,而且保证了日志完整度最高的节点才能当选的领导者

而且领导者的日志是唯一标准,用来实现日志的一致性

领导者收到了大多数的复制成功的响应后,会将日志应用到状态机中,返回成功的响应,但是如果有节点不在大多数中,接收日志失败,如何保证日志的一致呢

这个问题就是上面说的保证日志一致性的流程,在重新连接上网络后,会重新的接收日志项,这时候领导者发现这个follower的日志接收错误,会逐步的回滚测试,一致到能够匹配上的节点,关于这个逐步的回滚操作,是否可以仿造Mysql的GTID,来让follow返回的时候直接带着最新的GTID,方便主节点一口气将缺失的补全

发表评论

邮箱地址不会被公开。 必填项已用*标注