Raft协议--日志复制--03

Raft协议–日志复制–03


1、日志的组成

  1. 由 TermId,LogIndex,LogValue 组成
    1. 日志由有序编号(log index)的日志条目组成。
    2. 每个日志条目包含它被创建时的任期号(term)和用于状态机执行的命令。
  2. (TermId,LogIndex)能确定唯一一条日志
  3. 如果一个日志条目被复制到大多数服务器上,就被认为可以提交(commit)了。
    1. 大多数:(服务器节点总数/2)+1

在这里插入图片描述

上图显示,共有 8 条日志,提交了 7 条。提交的日志都将通过状态机持久化到磁盘中,防止宕机。

2、日志复制的过程

客户端的一条指令到达,Leader把将指令作为日志条目(Log entries)加入到它的日志中,然后并行的向其他Follower发起 AppendEntries(附加条目)RPC复制日志条目。当这条日志被复制到大多数服务器上,Leader将这条日志应用到它的状态机(比如如果是mysql的insert,那么就是执行insert操作),并向客户端返回执行结果。

  1. 客户端的每一个请求都包含被复制状态机执行的指令。

  2. leader把这个指令作为一条新的日志条目添加到日志中,然后并行发起 RPC 给其他的服务器,让他们复制这条信息。

  3. 假如这条日志被安全的复制,领导人就应用这条日志到自己的状态机中,并返回给客户端。

  4. 如果 follower 宕机或者运行缓慢或者丢包,leader会不断的重试,直到所有的 follower 最终都复制了所有的日志条目。

在这里插入图片描述

2.1、复制状态机

2.1.1、概念

  1. 对于一个无限增长的序列 a[1, 2, 3…],如果对于任意整数i,a[i]的值满足分布式一致性,这个系统就满足一致性状态机的要求
  2. 基本上所有的真实系统都会有源源不断的操作,这时候单独对某个特定的值达成一致显然是不够的。为了让真实系统保证所有的副本的一致性,通常会把操作转化为 write-ahead-log(WAL)。然后让系统中所有副本对 WAL 保持一致,这样每个副本按照顺序执行 WAL 里的操作,就能保证最终的状态是一致的

2.1.2、步骤

在这里插入图片描述

  1. Client 向 leader 发送写请求
  2. Leader 把"操作"转化为 WAL 写本地 log 的同时也将 log 复制到所有 followers
  3. Leader 收到多数派应答,将 log 对应的"操作"应用到状态机
  4. 回复 client 处理结果

2.2、动画过程

客户端发起一个SET 5的请求

这条日志还没被其他任何节点接收,所以它的状态是uncommitted。

在这里插入图片描述

Leader会将这条日志通过心跳消息复制给其他的Follower节点

在这里插入图片描述

一旦有大多数节点成功写入这条日志,那么Leader节点的这条日志状态就会更新为committed状态,并且值更新为5:

在这里插入图片描述

Leader节点然后通知其他Follower节点,其他节点也会将值更新为5。如下图所示,这个时候集群的状态是完全一致的,这个过程就叫做日志复制(Log Replication):

在这里插入图片描述

2.3、日志复制 关键点

  1. 连续性:日志不允许出现空洞
  2. 有效性:
    1. 不同节点,拥有相同 term 和 logIndex 的日志 value 一定相同
    2. Leader 上的日志一定是有效的
    3. Follower 上的日志是否有效,通过 leader 日志对比判断

2.4、Followers 日志有效性检查

  1. AppendEntries RPC 中还会携带前一条日志的唯一标识(prevTermId,prevLogIndex)
  2. 递归推导

2.5、Followers 日志恢复

Leader 将 nextIndex 递减并重发 AppendEntries,直到与 leader 日志一致

3、日志的一致性

3.1、日志复制的两条保证

  1. 如果不同日志中的两个条目有着相同的索引和任期号,则它们所存储的命令是相同的

    1. 原因:leader 最多在一个任期里的一个日志索引位置创建一条日志条目,日志条目在日志的位置从来不会改变
  2. 如果不同日志中的两个条目有着相同的索引和任期号,则它们之前的所有条目都是完全一样的

    1. 原因:leader每次 RPC 发送附加日志时,会附加这条日志之前日志的索引和任期号一起发送给 follower
      1. 如果 follower 发现和自己的日志不匹配,那么就拒绝接受这条日志,这个称之为一致性检查
      2. 如果 follower 发现和自己的日志匹配,才会附加上去。

3.2、日志的不正常情况

  1. raft是通过跟随者强制复制领导者的日志来保证的。

  2. 一般情况下,Leader和Followers的日志保持一致,因此 AppendEntries(附加条目)一致性检查通常不会失败。然而,Leader崩溃可能会导致日志不一致

    1. 旧的Leader可能没有完全复制完日志中的所有条目。
  3. 下图阐述了一些Followers可能和新的Leader日志不同的情况。一个Follower可能会丢失掉Leader上的一些条目,也有可能包含一些Leader没有的条目,也有可能两者都会发生。丢失的或者多出来的条目可能会持续多个任期。

在这里插入图片描述

如上图,对于a到f,最终都会和leader同步。

d:索引11的日志有,但是leader没有
f:索引4的任期是2,leader是4

其实就是通过日志覆盖解决。但是对于日志覆盖,我们就会想到一个问题,会不会覆盖已经提交的日志(日志对应指令已经返回给客户端)。那当然不会,如果真有这样,就会有不一致,或者指令丢失现象。

3.3、如何保证日志的正常复制

  1. Leader通过强制Followers复制它的日志来处理日志的不一致,Followers上的不一致的日志会被Leader的日志覆盖。Leader为了使Followers的日志同自己的一致,Leader需要找到Followers同它的日志一致的地方,然后覆盖Followers在该位置之后的条目。

  2. 具体的操作

    1. Leader会从后往前试,每次AppendEntries(附加条目)失败后尝试前一个日志条目,直到成功找到每个Follower的日志一致位置点(基于上述的两条保证),然后向后逐条覆盖Followers在该位置之后的条目。
  3. 总结

    1. 当 leader 和 follower 日志冲突的时候,leader 将校验 follower 最后一条日志是否和 leader 匹配,如果不匹配,将递减查询,直到匹配,匹配后,删除冲突的日志。这样就实现了主从日志的一致性。

4、Commit Index 推进

4.1、CommitIndex(TermId, LogIndex)

  1. 所谓 commitIndex,就是已达成多数派,可以应用到状态机的最新的日志位置
  2. 日志被复制到 followers 后,先持久化,并不能马上被应用到状态机
  3. 只有 leader 知道日志是否达成多数派,是否可以应用到状态机
  4. Followers 记录 leader 发来的当前 commitIndex,所有小于等于 commitIndex 的日志均可以应用到状态机。

4.2、CommitIndex推进

  1. Leader 在下一个AppendEntries RPC(也包括 Heartbeat)中携带当前的 commitIndex
  2. Followers 检查日志有效性通过则接受 AppendEntries 并同时更新本地 commitIndex,最后把所有小于等于 commitIndex 的日志应用到状态机

5、AppendEntries RPC

5.1、完整信息

  1. currentTerm
  2. logEntries[]
  3. prevTerm
  4. prevLogIndex
  5. commitTerm
  6. commitLogIndex

5.2、currentTerm,logEntries[]

日志信息,为了效率,日志通常为多条

5.3、prevTerm,prevLogIndex

日志有效性检查

5.4、commitTerm,commitLogIndex

最新的提交日志位点(commitIndex)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值