Raft协议详解(三)日志复制

在这一部分,我们讲Raft几个子问题中的日志复制问题。主要内容是讲Raft为什么要进行日志复制,以及如何进行日志复制的。日志复制(log replication)是leader的主要工作之一。在前面的第一、二部分,我们讲到了日志(log)是Raft的一致性保证非常重要的组成部分,很大程度Raft是利用log做的数据一致性。那么我们就来说说日志。


日志是啥

这里写图片描述

上图是Raft论文中的图片,我们可以之间看。每台服务器上都有自己的本地log,图中一行是一个服务器。对于一台服务器上的log,可以理解为一个list。list里面有两个元素,一个是term,一个是操作。结构大概是这样:

{
   term1:operation1,
   term2:operation2
   ……
}

当然,list也有索引(log index),如图最上面的1,2,3,4……。那么日志就长这样,保存着每个任期的每个操作。一个“term1:operation1”组合,被叫做一个entry。当leader认为log里面保存的entry操作可以被执行时,那么就会执行log里面的操作了。

假如说,每台服务器上的log内容都是一样的,那么执行log操作(也叫提交(commit))的时后,服务器里面的状态机(state machin,见Raft协议详解一)的内容也一定是一样的,因为状态机的内容就是log执行的结果。换句话说,我只要保证了每台服务器上的log内容一致,那么也就保证了数据(状态机内容)的一致了。

那么,如何保证日志一致呢?

日志的一致性保证

在Raft协议中有两个主要的消息,一个是在第二节讲到的RequestVote RPC,用于选主投票时leader发出的消息。一个就是AppendEntries RPC,用于心跳和日志复制。对于心跳,只需要发送空内容的AppendEntries RPC就可以了,我们主要关注日志复制的消息,看看Raft是怎么操作的。

1.leader接受客户端的操作请求,如“将X复制为3”。假如leader当前的任期为term1,那么leader就会向自己本地log的索引K的位置添加一个entry:“term1:X赋值为3”。之所以添加到K是因为K索引位置之前已经有了log内容了。
2.leader向集群中其他follower并行发送AppendEntries RPC消息。注意点来了!敲黑板。关键就是这个消息里面包含哪些内容。有两部分组成。一:这个新的entry:“term1:X赋值为3”。二:K-1,以及K-1索引位置的entry。
3.当一个follower收到一个AppendEntries RPC消息时会查看自己本地的log中的K-1位置的entry的内容。

  • 假如本地log中K-1位置的entry内容与接收到的来自leader的K-1的entry内容一致,那么就将leader发来的K位置的entry保存在自己的K位置(当然要做并发控制),并返回true,告诉leader保存成功了
  • 假如本地log中K-1位置的entry内容与接收到的来自leader的K-1的entry内容不一致,那么就返回false,告诉leader不一致。leader收到消息后,会将K减小一点,然后再次重新发AppendEntries RPC,直到follower返回true。因为到K=0的时候肯定会一样(都没有),因此早晚一定会得到true的回复。此时leader将匹配的位置和最新的位置中间的内容都发送给follower,follower会将接收到的内容覆盖到对应的位置。

4.经过第3步的操作,此时follower和leader的日志就已经一样了。

当leader收到了大多数的follower的true的返回,那么leader就可以回复客户端,已经成功更新了数据。

在上面第3步的过程中,需要不断去尝试K的正确位置,因此会发送比较多的AppendEntries RPC的消息。所以是可以在此处进行优化的。

其他

在Raft中,一切以leader为主。因此日志不是最新的话,就不能成为leader。因此在选主的时候,会进行日志比较。假如在投票阶段,一个follower收到的选主请求中,包含的日志信息比自己的要旧,那么也会拒绝给这个请求投赞成票。如何比较新旧呢?一是看任期term,一是看最后一个entry的索引号。任期大的新,任期相同的索引大的新。

此外,假如leader在准备commit一个得到大多数follower的true的entry时,宕机了。那么后面的leader如何保证前面的leader的entry被commit呢?实际上,一条 entry被准备commit的前提是,它已经被复制到了多台服务器上了。由于leader的选举,会选择有比较新的log的服务器,那么拥有上一个leader的entry的,那一定时集群中最新的。也就是说拥有上一个leader的entry内容的服务器才能成为leader。那么后面的leader在不知道上一个leader有没有提交的情况下,怎么操作呢?(我猜可以用一个变量保存上一次执行到哪个log index位置了,后面的接着执行。具体我没看懂论文里怎么说的)

以上就是日志复制问题了。后面我们讲第三个:维护成员变化(membership changes)

猜你喜欢

转载自blog.csdn.net/Snail_Ren/article/details/80620879