raft算法中,5.4.2节一疑问( 二 )


如果S1提交了这条数据,用户就会可见。但是S1突然挂了,S5当选为leader(currentTerm是5),继续复制index 为2的log,注意,假设允许leader直接commit old term满足多数派的log,那么S5会继续复制term为3,index为2的log,导致之前用户可见的index为2的log被覆盖(也就是S1以term2执行的复制)。这会打破数据的一致性,一条用户可见的数据没了。
到这里就可以推导出,leader并不能直接commit 虽然满足多数派,但是不是当前term复制的日志。
那么如何避免呢,接着往下看
However, if S1 replicates an en-try from its current term on a majority of the servers beforecrashing, as in (e), then this entry is committed (S5 cannotwin an election). At this point all preceding entries in the logare committed as well如果S1在crash前,以当前term(4)成功提交了一条index=3的日志,那么index=2的日志也会被间接提交(详情见AppendEntriesRpc的实现)。此时即使S1挂了。S5的term=3,不满足选主条件,也就没法覆盖index=2的日志了。
===========================================================
结论,leader决定某条日志是否可以提交的充分条件:
1)这条日志满足多数派。
2)至少需要以当前term提交了一条日志。

■网友
这幅图的意思不是说(e)行,但(d)不行。而是说(e)和(d)都是(c)之后有可能会发生的情况
这幅图想说明的就是为什么当前term不会提交之前的term的日志条目,只会提交当前自己所在的term下的日志条目。如果提交了之前的term的log会发生什么后果。
在处于(c)的这一步的时候,我们不能提交term 2的log的原因是有可能会出现情况(d)。在情况(d)里面term 2的log被overwritten了,但是已经提交的log是不允许被overwritten的。
作者在他的PhD论文上关于这个问题用了一副更加易懂的图:
raft算法中,5.4.2节一疑问


■网友
这个其实是由leader选举过程存在的缺陷引起的。

leader选举的目标是:选举出一个包含所有过半提交日志的server来作为leader,条件如下2个(条件1优先条件2):
1 term越大,该server越容易成为leader
2 term相同的情况下,index越大,该server越容易成为leader
按照上述选举方式,如果大家term都一样则选举出来的leader必然包含所有过半提交日志,但是如果term不一样,则按照上述选举方式选出来的leader可能并没有包含所有过半提交的日志,正如题主所讨论的场景。

所以说这种选举PK的方式中的条件1是存在缺陷的,如何来解决呢?
解决办法的本质就是消除条件1可能导致的误判,在条件2下进行PK选举

Raft的解决方式:那么就需要对过半提交加以限制,多加1个条件即当前term不会提交之前的term的日志条目。一旦加上了这个限制意味着含有所有过半提交的日志的几个server必然拥有目前最大的term,这样的话再次进行leader选举时,就避免了条件1的误判,在条件2下进行PK

你会想到ZAB其实也是类似的2个条件来进行PK选举的,ZAB为啥不存在Raft的这种限制?
ZAB在leader选举出来后会立即进行数据同步过程,同步下最新的epoch(相当于term),也是避免了条件1的误判,数据同步之后就不会存在Raft(经过几个term变更后)日志错乱的现象。ZAB是正式对外提交服务之前先清理好数据的不一致问题,Raft是延迟清理的,在后续的复制过程中不断的进行纠正更新

■网友
因为2,3可能是不同指令啊
■网友
前提条件:已经commit的entry不能被重写。我的想法是:如果日志中entry1被重写为entry2,会导致状态机中的状态(entry1)与log中的entry2不一致。


推荐阅读