前言
总结raft中一些重难点,首先raft解决的是分布式系统选主和主从复制的问题。分布式的扩容分片问题不属于raft范畴,但分布式系统中恰恰是一致性最难保证。
最近看了几篇解析raft的文章,关于论文的解读有几个点都没说清楚,查找资料几天总结一下,主要疑点在安全性那个章节。
客户端请求Leader后Leader和Follower交互流程
这里看了很多博客没有体现出来!导致在选举限制那理解障碍,恰好这里太重要了!!!以一次完整的客户端请求来总结整个过程为例,包括客户端发送请求和Leader在什么时候响应;假设集群有3个节点:A、B、C,其中A当前为Leader,一次完整的请求的过程如下:
1、客户端提交到请求至Leader
2、Leader自己先保存日志,注意这里不提交
3、Leader将日志同步给Follower,这里只画了1条线,即只同步给C,实际上是都会同步
4、Follower收到日志后保存日志并响应给Leader
5、Leader只要收到一个Follower的响应后马上将这条日志提交并应用到状态机中
6、Leader应用到状态机完成后就可以返回给客户端了
上图中后续的流程没有画,即Follower在什么时候提交日志?
Leader在下一次心跳的时候会将最新的commitIndex带上,Follower因此提交日志并应用到状态机中
为什么不直接在主节点同步到从节点时,直接让从节点标记为提交?
这里其实就是一个事务问题了,假如总节点n,当n/2 + 1节点提交成功了才算提交成功,这明显是一个分布式事务问题。首先在这里引入分布式事务性能太低效了,而且复杂度太高,所以没采用分布式事务类似方案。
再来看看选举限制
第一个限制条件
3.6.1 选举限制
在任何基于领导者的一致性算法中,领导者都必须存储所有已经提交的日志条目。在某些一致性算法中,例如 Viewstamped Replication,某个节点即使是一开始并没有包含所有已经提交的日志条目,它也能被选为领导者。这些算法都包含一些额外的机制来识别丢失的日志条目并把他们传送给新的领导者,要么是在选举阶段要么在之后很快进行。不幸的是,这种方法会导致相当大的额外的机制和复杂性。Raft 使用了一种更加简单的方法,它可以保证所有之前的任期号中已经提交的日志条目在选举的时候都会出现在新的领导者中,不需要传送这些日志条目给领导者。这意味着日志条目的传送是单向的,只从领导者传给跟随者,并且领导者从不会覆盖自身本地日志中已经存在的条目。
Raft 使用投票的方式来阻止一个候选人赢得选举除非这个候选人包含了所有已经提交的日志条目。候选人为了赢得选举必须联系集群中的大部分节点,这意味着每一个已经提交的日志条目在这些服务器节点中肯定存在于至少一个节点上。如果候选人的日志至少和大多数的服务器节点一样新(这个新的定义会在下面讨论),那么他一定持有了所有已经提交的日志条目。请求投票 RPC 实现了这样的限制: RPC 中包含了候选人的日志信息,然后投票人会拒绝掉那些日志没有自己新的投票请求。
Raft 通过比较两份日志中最后一条日志条目的索引值和任期号定义谁的日志比较新。如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。
这里开始就确定了raft的原则:
- 领导者都必须存储所有已经提交的日志条目
- 同时数据只能从领导者传到跟随者
这里有一个疑问,这里的最后一条是已经标记提交的最后一条,还是日志文件的最后一条?
Raft 通过比较两份日志中最后一条日志条目的索引值和任期号定义谁的日志比较新。如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。
根据之前的结论Leader在下一次心跳的时候会将最新的commitIndex带上,Follower因此提交日志并应用到状态机中可得出:即使Leader节点已经提交了日志,Follower节点其实也不能及时感知。那么会存在下面情况:
term1 A是主节点收到了客户端的数据1并成功同步数据1,BC也收到心跳提交了了数据1
A*: 1 B: 1 C: 1
term1 A还是主节点收到了客户端的数据2并写入了B得到了响应,此时满足n/2 + 1,A将2标记为已经提交并返回给我客户端成功,客户端已经可以查到2了,但还没等到A写入C以及发送心跳给B告诉B自己已经将2提交,就挂掉了
A*: 1 2 B: 1 2 C: 1
此时B是不知道2已经提交的,那么开始重新选举会选谁?
如果选举限制中的最后一条日志是已提交的最后一条的话,那么term2 C可以当选,因为大家已提交都是term1的数据1,但如果C当选了就会有问题,C收到新的数据3后就会同步给B将在相同index下的2给覆盖掉了,此时客户端再查发现刚才的2不见了!!!
如果选举限制中的最后一条日志日志文件最后一条的话
那么term2 C必不可能当选,因为B的term1虽然相同但是index为2大于C所以他拒绝给C投票,最后当选的就是B,B当选后发现2没提交,会将他写入C,这里写入C后B也不能直接提交,原因见后。但此时客户端看到的2不会被覆盖!!!
所以结论是选举限制是比较的日志文件的最后一条而不是已提交的最后一条!
虽然Follower不能及时感知Leader的提交,但通过限制选举能控制只要你复制过半,没有复制到的节点就不可能当选Leader,因为他没有最新的term!!!
第二个限制条件
下面这个图讨论太多了感觉讲的都有问题,首先给出结论这个图并不是说d1不对,d2是对的,其实他们都可能是正确结果,取决于C中2是否提交!
如果c处提交了2走到d1就是错的【注1】,如果没提交走到d1就是正确的。
如果c处提交了2走到d2没问题,即使没提交走到d2一起提交也没问题。
所以得出下面结论: 不能提交之前任期内的日志条目要等到新的任期数据来了复制完成才能一起提交!!!
【注1】这里就是上面说的客户端感知到2存在但是S2,S3没有收到心跳更新提交index,到d1中结果2被覆盖了
这里之前有人问c到d1为啥S5能成为Leader?
有了之前的结论就可以得出C中S1挂掉了S5是可以选成主的而且是一定成为主节点的,因为选举比较的是最新的index和term明显S5的term大