raft论文(三)

Log replication

Commited日志

首先引入Commited日志的概念,那么什么样的日志是Commited日志呢?
Committed:
Leader会决定什么样的日志entry应用到状态机是安全的,这样的日志entry被称为committed。如果一个日志entry被复制到大部分的server,那么这个日志就是一个committed

Commited的日志会有如下的特性:
如果这个日志被这个Leader标记为commited,那么这个日志之前的日志也都是commited注意:这里之前的日志,可能是本Leader提交,也可能是其他的历史Leader提交的日志

Leader决定了一个日志的Committed状态后,就会维护这个日志entry的索引,在后续的RPC中(包括heartbeat),就会把这个索引值附加进去,那么其他的Followers就可以知道这个entry是可以被应用到状态机中。

日志复制过程

日志Entries
日志复制提交过程:

  1. Leader接受到来自客户端的命令;
  2. Leader将entry Append到日志中,并且给其他的Followers发送AppendEntry RPC;
  3. 收到来大部分Follower的回复后,Leader Commit该日志(更新commitIndex),将日志应用到状态机(更新lastApplied),给客户端回复;
  4. 在Leader的下一个AppendEntry RPC中,会额外包含Commited日志的信息(即自己的commitIndex);
  5. Follower接收到Commited日志的信息后,会应用到状态机。
    因此对于上图所有,Commited Entries是1~7,index8只被复制到2个server。

一致性检查

raft日志具有如下两个特性(Log Matching):

  1. 在不同的日志中,如果两个entries具有相同的index和term,那么这两个entry存储了相同的内容;
  2. 在不同的日志中,如果两个entries具有相同的index和term,那么这两个entry的所有前继日志也是完全相同的;

第一个特性的保证:Leader在一个term内,只会在一个index位置出创建一条日志,且这个日志一旦被创建后,就不会再被更改;
第二个特性的保证:**一致性检查:**每次Leader发送AppendEntryRPC时,PRC都会包含最新日志的直接前继日志的index和term,如果Follower发现自己没有这个index和term的日志,那么就会拒绝。
初始化时,满足一致性检查;当日志被Append成功,一致性检查是通过的,Leader就知道Follower的日志和自己是一样的。

一致性检查失败

Leader日志和Follower不一致的情况,可以用下面这个图来列举:

日志不一致
term = 8阶段,新Leader产生,新Leader的日志为1~10,会存在以下几种情况:
a-b: Follower日志缺失,比如老Leader还没有来得及将日志复制过去,就发生了崩溃;
c-d:日志比新Leader多,比如老Leader还没有来得及将日志复制给新Leader(那个时候它的角色还是Follower),但是已经复制给了c(d),发生了奔溃;
e: term4是Leader,但没能将index6,index7复制到其他的Follower就发生了奔溃,后续就一直没有上线;
f:term2是Leader,但是没能将term2期间的日志复制到其他的Follower发生了奔溃重启,term3又当选了Leader,但是还没能将term2和term3的日志复制到其他Follower又发生了奔溃,后来就一直没有上线;

不一致日志的处理:
强制Follower复制自己的日志来解决,这就意味着,Follower中的冲突日志,可能不会覆盖。
额外的限制:
具体的做法:

  1. 成为新的Leader后,对每一个Follower维护一个nextIndex,这表示下一个需要发送给Follower的日志index。对于Figure7,新Leader获选后,nextIndex = 11;
  2. 在下一次AppendEntry RPC中,会包含用于一致性检查的信息(index = 10, term = 6);
  3. 以e为例,一致性检查失败,Leader会nextIndex--并进行重试,直到index = 5, term = 4时,一致性检查才通过;
  4. 此时会删除e冲突的日志,append Leader包含的日志。此后,在这个任期内,e的日志就会和Leader的保持一致。

基于以上的机制,与Leader的不一致就会在多次AppendEntries RPC的一致性检查得到收敛,这个过程中修改的都是Followers的日志。Leader从来不会覆盖或者删除自己的日志(Leader Append-only)。

问题讨论

问题一

Leader决定了一个entry的状态为Commited的详细流程,以及之后所做的动作?

Leader:
初始化时
Logs:
logs

commitIndex = 0;
lastApplied = 0;
nextIndex[i] = 1;
matchIndex[i] = 0;  // i 指的是对于所有的raft节点

成为Candidate后,构建RequestVote RPC:

RequestVoteArg {
    Term = 1,
    CandidateId = me,   // me指自身的节点id
    LastLogIndex = 0,
    LastLogTerm = 0,
}

成为Leader后,接受到Command,首先将entry追加到自身的logs中:
logs
更新index:

matchIndex[me] = 1;  // 因为自身直接append给自己的log了
nextIndex[me] = 2;

给其他的raft节点发送AppendEntry,构建AppendEntry RPC:

AppendEntrryArg {
    term = 1,
    LeaderId = me,
    Entries,
    LeaderCommit = 0,
}

LeaderCommit被赋值为commitIndex,当Leader的commitIndex更新时,会通过AppendEntry带给Follower。
RPC成功,更新对应的index:

matchIndex[i] = 1;
nextIndex[i] = 2;

假设五个节点的RAFT集群,3个更新成功,2个没有更新成功,即多数更新成功, commitIndex会根据多数节点的matchIndex的值被更新: commitIndex = 1
commitIndex更新为1后,触发apply日志:

for lastApplied < commitIndex {
    lastApplied++;
    applyLog;
}
lastApplied = 1;

Follower:
Follower收到AppendEntry,会将RPC中携带的logs append到自己的log中。
且如果RPC中携带的LeaderCommit比自己的commitIndex大,就更新自身的commitIndex,然后apply自己的日志,更新lastApplied

问题二

日志已经被应用到大多数节点的状态机,此时一个没有复制该日志的节点当选leader(这种情况是否会发生)?此时一致性检测,是否会删除其他节点的日志?这种情况是如何处理的?

回答:
没有复制这个日志条目的节点,由于最新的日志条目没有其他的节点新,因此在elect leader request vote的时候,会被其他的节点拒绝。下面的章节也会说到这个问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值