是什么
raft通过集群的方式为客户提供强一致、高可用的服务。强一致表现在客户端看的都是最新的数据。高可用性表示不管是是网络故障还是节点宕机都不会影响客户端对最新数据的获取。主要通过副本读取LOG的方式进行,根据日志中记录的操作来更新状态。
工作状态
Raft在正常工作状态下如上图所示,在一个raft group中只有一个leader,由leader负责客户端对raft数据的读写,当客户端有数据写入需求的时候交给leader,然后leader通过LOG的方式将数据变化发送给其它节点(follower),只有在超过半数以上的节点确认写入的情况下才能commit并且返回给客户端成功的信息。follower和leader之间通过心跳获取各自的状态。
选举
在leader因为网络或者主机宕机等原因发生故障,follower在一定时间内未收到来之leader的心跳信息,follower就会认为leader发生了故障进而启动选举程序进入candidate状态,开启新一轮任期(term=当前term+1)。首先会给自己投票一票,然后向其它节点发送拉票信息,其它的节点判断是否同意。有如下情况会导致其他节点投拒绝票:
1、已经投过赞成票了,一个节点只能投票一次
2、其它节点的term或log比发起拉票节点的高。
根据投票的情况会出现3中结果,自己赢得选举、其它节点赢得选举、未产生结果。
自己赢得选举,当一个candidate节点获得超过半数的赞成票时候这个节点就赢得选举成为leader,节点的投票是根据先到先得原则进行投票的,谁先向我拉票就把票给谁。
他人赢得选举,没赢得过半票数,或者在选举的过程中受到了leader的信息(term比自己大)那么自己就变成follower。
未产生结果,如果两个节点同时发起投票,首先自己给自己投一票,然后就没有票给其它人了,这样产生的结果就是没人获得半数选票,在这种情况下raft会要求各个节点在一个随机时间之后再次发起投票(term+1),因为两个节点发起再次投票的时间不同,所以就不会出现没票的情况。
raft log格式
每条日志保存了对于的数据变更,以及日志编号和term号码。leader在发生log给follower的时候还会发送上一条日志的信息(序号和任期),如果follower发现接收到的log中记录的上一条log的信息和自己当前的不一致,说明中间存在数据差异,这个时候follower就会要求leader发生缺失的log。
选举的其它问题
数据不一致问题
从图中可以看出,在完成了选举之后有的follower比leader的数据多,有的比leader的数据少。用f为列,看看什么情况下会发生这种情况:
在任期2的时候f为leader,这个时候写入了3条数据,但是没有提交就被下线了,在任期3的时候又上线,然后由写入了5条数据,之后由其他节点成为leader。新的leader会检查出这个数据的不一致,然后对这个不一致就行修复,具体的做法的依次向前比对数据,直到找到重合的数据为止,在图中是log=3的位置,然后重新发送log=3~10的所有数据。总结起来就是leader会要求所有的follower和自己的数据保持一致。
数据覆盖
还有一种情况,可能会导致即使多个超过半数的节点已经提交了日志,依然有可能被其他新选的Leader覆盖日志
1、在a状态下,S1为leader,当前的term=2,只是完成了部分日志写入(没有同步到s3/s4/s5),这个时候S1被下线了。
2、在b中S5被选S4、S3选举为新的leader(因为S5获得了S3、S4的选票就可以成为leader),并且S5在log=2的位置接收到了新的请求(term=3)
3、在c的情况下,S1上线同时有被选举为leader,并且在log=3的时候做了数据的写入(term=4),同时因为leader做数据校验的时候,同步了log=2(term=2)给s3,这个时候log=2的数据已经满足提交的条件了(超过半数),但是这种情况下是不能提交的。
4、假设在上述情况下发生了提交,那么如果S1再次下线,那么S5肯定会被选择为leader(因为term最大),S5作为leader之后就会覆盖之前做的提交(log=2的数据)也就造成了数据异常。
5、怎么避免?可以限制leader只能提交当前term的数据,在上述的情况下,不提交log=2的数据(log=2的数据是term=2的时候写入的)提交log=4(term=4)的数据,就可以解决,因为这种情况下就算S1下线,S5也不会被选举为新的leader因为其它的节点的term比S5的term大~