raft算法介绍

raft算法中,写入请求的一般执行过程:

  1. 接收到写入请求的node,将请求转发给leader。
  2. leader将写入请求记录到leader自己的log中。
  3. leader向其他节点发起append请求,可能包含旧数据的append。
  4. leader确认半数以上的node已经成功append之后,执行commit。
  5. commit之后可以执行apply,也就是执行写入请求,apply完成之后,认为请求执行成功,返回。

leader节点fail后,如何产生新的leader

  1. 每个node都和leader之间保证着心跳,leader节点fail后,其他node接收不到心跳,超时后,就会成为candidate。超时时间每个node是不同的,一定程度避免发生竞争。
  2. candidate向其他node发送RequestVote请求,其他node根据情况判断能否执行这个RequestVote请求,并返回成功/失败
  3. candidate如果得到半数以上的vote,则成为leader

集群中的leader应当是唯一的,在不同的时间点leader可能不同,但在一个时间点,leader应当只有一个。准确的说,可能会存在多个leader,但能够成功执行请求的leader,应当只有一个,其他存在的leader可能是因为消息滞后(例如网络断开导致),没有及时退位成follower。

如何保证leader的唯一性呢——得到半数以上的node的vote,才能成为leader,那么就只有一个leader了。但是leader fail后,需要重新vote,那么follower如何知道可以重新vote呢?这里就引入了term,就相当于是一个逻辑时钟,一个term就是一个时间段,对应一个leader的任期,follower在一个term进行了vote之后,就不再有在这个term进行vote的权力了,只有等到下一个term,才能够进行vote。

加入了term之后,流程变为:

  1. 每个node都和leader之间保证着心跳,leader节点fail后,其他node接收不到心跳,超时后,就会成为candidate。超时时间每个node是不同的,一定程度避免发生竞争。
  2. candidate本身有记录当前的term,将term+1,向其他node发送RequestVote请求,请求中带了term作为参数。收到请求的node,判断请求中带的term是否比自身记录的要大,如果要大,就返回成功,并将自身记录的term修改为请求中的term。否则返回失败。
  3. candidate如果得到半数以上的vote,则成为leader

在这里插入图片描述
上述是论文中的条件1,条件2是为了保证数据的完整性,这个后续分析。

另外几个注意点:

  1. node记录的当前term,记为currentTerm,需要记录到硬盘或其他存储介质中,保证重启不丢失,以避免重启前后对同一个term进行两次vote。
  2. leader和candidate,在发现后比自身的currentTerm更大的term时(收到请求时可能发现),转换为follower。

如何保证切换leader后数据一致

我们再看正常情况下的写请求执行过程:

  1. 接收到写入请求的node,将请求转发给leader。
  2. leader将写入请求记录到leader自己的log中。
  3. leader向其他节点发起append请求,可能包含旧数据的append。
  4. leader确认半数以上的node已经成功append之后,执行commit。
  5. commit之后可以执行apply,也就是执行写入请求,apply完成之后,认为请求执行成功,返回。

每个node都会有一个log,顺序记录了所有的操作。为什么有log呢?因为每次请求执行的时候,都是有可能会失败的,如果node收到请求直接执行操作(也就是apply,例如写入到数据库中),那么失败之后,还需要进行回滚操作。但如果是记录log的话,发现失败了就删除log即可,只有确定操作会执行成功之后,才将当前操作apply。

那么,这个log就决定了我们执行操作的内容与顺序,保证这个log的一致性,也就能保证最终数据的一致性了。

在这里插入图片描述
论文中总结了log需要满足的一些属性(图中后面4点):

  1. Leader Append-Only: leader只会执行append操作,也就是追加log,不会覆盖/删除log。而follower是会删除的,当follower发现自己的log和leader不匹配时,就会删除log。
  2. Log Matching: 任意两个node,如果各存在一条log,他们的序号和term都相同,那么他们的内容是相同的,且所有前序的log也都相同。
  3. Leader Completeness: 如果一个log已经执行commit了,那么后续的所有leader都会包含这个log。
  4. State Machine Safety: 如果一个log已经apply了,那么所有node都会apply这个log,准确说可能是在一段时间后apply,也可能不apply(一直断网/一直关机),但不可能apply另一个log(另一个log指index相同,term或者log内容不同)。

第2点,和node执行append请求的策略(找到和leader相同的log,把后面不匹配的log都删除掉),保证了所有的node的log最终都是趋于一致的。
第2点则是需要第1点来保证,只会进行append,而且append的log如果term和index相同,那必然是唯一的(由leader的唯一性保证),而append到follower之前,需要先保证前序log都相同才能append,那么就能保证第2点了。

第3、4点,则是为了保证整个集群的状态一致,不会因为leader发生变化,出现数据发生变化的情况,例如选举出了新的leader,leader的数据不完整了,之前能够读取出的数据现在却读取不出来了。

如何保证第三点?需要对leader选举增加限制:
在这里插入图片描述

第二点,要求candidate必须比其他node的数据更新,才能得到vote,那么如果成为了leader,就能保证candidate跟半数以上的node的数据一样,或者更新。由于commit的log必须是在半数以上的node上执行了append,才能commit,那么成为leader的node必然会包含所有已commit的log了。

这里还有个情况需要说明一下,及时leader刚选举出来时,log是可能多出来的,也就是可能会包含没有commit的log,这个会对一些操作有影响。

一些实现细节:

  1. receiver只会接受term大于等于currentTerm的append请求,否则旧的leader可能会把新leader的append的数据修改掉。
  2. leader上记录nextIndex[]:对每个node下一个应该发送的log,leader刚被选出时初始化为最新的log+1,通过append请求是否失败进行更新
  3. 刚成为leader时,进行一次空请求的append entry操作,commit index会变成这个index,则所有之前的log都认为已经commit
  4. node收到leader的append请求(带commitindex)时,commitindex更新为leader的commitindex,如果本地log还没有到leader的commitindex,则更新为最新的match index

如何读取数据

读取数据需要保证什么特性
1.consistency
如果没有发生状态变化(执行新的写操作),那么读到的结果是相同的,不论从那个node进行读取,都是相同的。
2.linearizable。
读取时,可以读取到所有已经完成的写请求(请求已经返回成功了),不可以读到未完成的写请求。

log read

将read作为一个需要写log的操作,执行一遍后返回,这样肯定是没有问题的,就是性能比较差。

read index

还是从leader读,需要:

  1. leader确认自己还是集群的有效leader。
  2. 保证读取到所有已经commit了的请求执行后的结果。

如果存在commit了,还没有apply的log,需要等待这些log被apply了之后再执行读请求。虽然写请求是已经apply了才返回,但如果返回后切换了leader,新leader上并不一定是已经apply了的。

前面有提到,leader需要包含所有已经commit了的log,所以读leader就相当于是读集群的已经commit了的数据。

如何保证是有效leader?准确的说是保证收到读请求的那一刻,还是有效leader。一遍是发起一次心跳,得到半数以上的node回复确认自己还是leader,那么就是有效leader。

lease read

leader发起心跳时,带一个租期参数,也就是说当前时间点往后,在租期范围内,保证所有收到请求的node不会发起新的leader选举,也就不会产生新的leader。

那么在租期时间内,就不需要通过心跳来确认自己是否是leader了,节约了确认自己是leader的操作。

但是这需要一个前提,就是每个node之间的时间相同;或者相差很小存在上限,并调整租期时长保证不会租期内不会选出新leader。

读follower

读follower,是不满足一致性的,follower并不能保证已经包含了最新的commit的log。

可以:
1.与leader进行一次交互,确认commitIndex是多少,等待apply到这个index之后再返回
2.如果需求是snapshot读,那么可以等待apply的log时间/序号超过指定的snapshot后再返回

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值