安全性

安全性

要保证所有的状态机有一样的状态,单凭复制与选举算法还不够。例如有3个节点A、B、C,如果A为主节点期间C挂了,此时消息被多数节点(A,B)接收,所以A会提交这些日志。此时若A挂了,而C恢复且被选为主节点,则A已经提交的日志会被C的日志覆盖,从而导致状态机的状态不一致。

Raft增加了如下两条限制以保证安全性:

  • 拥有最新的已提交的log entryFollower才有资格成为Leader
  • Leader只能推进commit index来提交当前term的已经复制到大多数服务器上的日志,旧term日志的提交要等到提交当前term的日志来间接提交(log index小于commit index的日志被间接提交

选主的限制

拥有最新的已提交的log entryFollower才有资格成为Leader

在所有的主从结构的一致性算法中,主节点最终都必须包含所有提交的日志。有些算法在从节点不包含所有已提交日志的情况下,依旧允许它当选为主节点,之后从节点会将这些日志同步到主节点上。但是Raft采用了简单的方式,只允许那些包含所有已提交日志的节点当选为主节点。

注意到节点当选主节点要求得到多数票,同时一个日志被提交的前提条件是它被多数节点接收,综合这两点,说明选举要产生结果,则至少有一个节点在场,它是包含了当前已经提交的所有日志的。

因此,Raft算法在处理要求选举的RequestVote消息时做了限制:消息中会携带Candidatelog消息,而在投票时,Follower会判断Candidate的消息是不是比自己“更新”(下文定义,如果不如自己“新”,则拒绝为该Candidate投票。

Raft会首先判断两个节点最后一个log entryterm,哪个节点的对应的term更大则代表该节点的日志“更新”;如果term的大小一致,则谁的log entry更多谁就“更新”。注意,加了这个限制后,选出的节点不会是“最新的”,即包含所有日志;但会是足够新的,至少比半数节点更新,而这也意味着它所包含的日志都是可以被提交的(但不一定已经提交

这个保证是在RequestVote RPC中做的,Candidate在发送RequestVote RPC时,要带上自己的最后一条日志的termlog index,其他节点收到消息时,如果发现自己的日志比请求中携带的更新,则拒绝投票。日志比较的原则是,如果本地的最后一条log entryterm更大,则term大的更新,如果term一样大,则log index更大的更新。

提交前一个term的日志

Leader只能推进commit index来提交当前term的已经复制到大多数服务器上的日志,旧term日志的提交要等到提交当前term的日志来间接提交(log index小于commit index的日志被间接提交

这里我们要讨论一个特别的情况。我们知道一个主节点如果发现自己任期(term)内的某条日志已经被存储到了多数节点上,主节点就会提交这条日志。但如果主节点在提交之前就挂了,之后的主节点会尝试把前任未提交的这些日志复制到所有子节点上,但与之前不同,仅仅判断这些日志被复制到多数节点,新的主节点并不能立马提交这些日志,下面举一个反例:

已提交的日志被覆盖

  • (a)时,S1当选并将日志编号为2的日志复制到其它节点上。
  • (b)时,S1宕机,S5获得来自S3S4的投票,当选为term 3的主节点,此时收到来自客户端的消息,写入自己编号为2的日志。
  • (c)期间,S5宕机而S1重启完毕,它重新当选为主节点并继续将自己的日志复制给S3,此时编号为2term2的日志已经被复制到多数节点,但它还不能被提交。
  • 如果此时S1宕机,如(d)所示,此时S5获得来自S2 S3 S4的投票,当选新的主节点,此时它将用自己的编号为2term3的日志覆盖其它节点的日志。
  • 而如果S1继续存活,且在自己的任期内将某条日志复制到多数节点,如(e)所示,则此时S5已经不可能继续当选为主节点,因此该日志之前的所有日志均可被提交(包括前任创建的,编号2的日志

上例中的(c)(d)说明了,即使前任的日志已经被复制到多数节点上,它依然可能被覆盖。因此Raft并不通过计算前任日志的复制次数来判断是否提交这些日志,Raft只对自己任期内的日志计数并在复制到多数节点时进行提交,且在提交这条日志的同时提交之前的所有日志。

Raft算法会出现这个额外的问题,是因为它在复制前任的日志时,会保留前任的term,而其它一致性算法会为这些日志使用新的termRaft算法的优势在于方便推理日志的形成过程,同时新的主节点需要发送的前任日志数目会更少。

网络分区容忍

根据前文介绍的Raft的两大安全性保障,我们能知道Raft天然兼容网络分区的情况。

Raft 网络分区

在开始的时候所有节点的LeaderB,而后发生了网络分区情况,独立的三个节点选择了C作为新的Leader。此时有两个客户端分别设置了不同的值38,节点B因为无法与绝大部分节点通信,因此属于不可提交状态;而新成组的3个节点会进行值的设置。

在网络分区被修复后,B接收到了更高的Electron Term因此退化为普通的节点,然后根据Leader上最新的日志回滚本地未提交的Entries

下一页