Raft算法导读

Posted 鸿的学习笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Raft算法导读相关的知识,希望对你有一定的参考价值。

导论

Raft算法是管理复制日志的一致性算法。

一致性的算法是让分布式系统表现的像单机系统一样,即使系统中有一些机器损坏了,也一样可以正常运行。

Raft设计出来是为了实现工程上的可用,避免Paxos算法的复杂性,从In Search of an Understandable Consensus Algorithm (Extended Version)这篇论文也可以看出,Raft部分原因也是为教学设计。论文很长,并且也已经有中译版,权且把这篇文章当作一篇导读。

复制状态机(Replicated State Machine)

在Raft算法中,集群像HDFS一样是主从架构,每一台机器的状态都唯一依赖于日志,而日志只来源于Master节点。日志中包含中一系列的命令,命令都包含着唯一编号,编号只会递增,不会减少或发生变更,而机器会根据命令不断地执行下去,所以只要日志保持一致,每台机器最终都会保证在统一状态。这也就是复制状态机。

问题分解

Raft算法将一致性问题拆分成了领导选取(leader election)、日志复制(log replication)、安全(safety)和成员变化(membership changes)四个小问题,分别给出了解答。

领导选取

如上文所述,Raft算法必须要有一个Master节点,那么Master节点必须要有一个选取地过程,以保证Master宕机后,也能在其它节点运行。Raft算法将集群中机器地角色分为了三种:

  • 领导人(Leader)

  • 候选人(Candidate)

  • 追随者(Follower)


三者地状态变化如下图:

简单来说,Follower会只会接收请求,当Follower接受不到请求时,会变成一个Candidate发起一次Election,获得绝大数票的Candidate会成为Leader。在Election的过程中会遵循下面几条原则:

  • 只有当一个Candidate得到过半的选票时才能赢得选举,每一个节点按照先到先得的方式,最多投票给一位Candidate。

  • 在等待投票结果的过程中,如果Candidate收到其他节点发送的心跳信息(AppendEntries RPC),并检查心跳信息中的任期不比自己小,则自己变为Follower,听从新上任的Leader的指挥。

  • 当未获得结果时,会重新发起一次Election,Raft为了避免wait时间过长,Leader不可用的超时时长随机获得,以避免同一时间宕机,同样的Candidate的拉票也是随机发送请求。

日志复制

Leader把每一条请求作为新的日志条目加入到它的日志中去,然后并行的向其他服务器发起 AppendEntries RPC ,要求其它服务器复制这个条目。当这个条目被安全的复制之后,Leader会将这个条目应用到它的状态机中并且会向客户端返回执行结果。如果Follower发生意外(崩溃、运行缓慢、网络丢包等),领导人会无限的重试 AppendEntries RPC(甚至在它向客户端响应之后)以保证所有的Follower最终存储了所有的日志条目。

日志的结构中不仅包含具体的操作,还包含本次插入的前一个位置的index和Term(任期),从而保证:

  • 如果在不同日志中的两个条目有着相同的index和Term,则它们所存储的命令是相同的。

  • 如果在不同日志中的两个条目有着相同的index和Term,则它们之间的所有条目都是完全一样的。

简而言之一句话:Leader负责一致性检查,同时让所有的Follower都和自己保持一致。

安全

有了上面所述,继而引发安全问题,如何保证日志是可靠的:

  1. 日志在传输过程中可能会因为种种原因导致日志发生缺失,所以并不是任何一个Follower都有资格成为Leader,因为如果一个Follower缺少了上任Leader已经commit的日志,那它是无论如何也不能做新的Leader的,因为这会导致数据的不一致。

安全性第一条限制
Candidate在拉票时需要携带自己本地已经持久化的最新的日志信息,等待投票的节点如果发现自己本地的日志信息比竞选的Candidate更新,则拒绝给他投票。

  1. 有可能即使多个超过半数的节点已经提交了日志,然后被其他新选的Leader覆盖日志。

安全性第二条限制
只允许Leader提交(commit)当前Term的日志。

成员变化

Raft将节点的增删抽象成配置的变更,当Leader收到配置变更的消息之后,它就将新的配置Cnew作为一个特殊的Raft Entry发送到其他的Follower上面,任何节点只要收到了这个Entry,就开始直接使用Cnew。当Cnew这个Log被 committed,那么这次配置变更就结束了。
值得注意的是:

  • 当Log里面有一个配置变更还没有被committed时,不允许接受新的配置变更请求,以防止脑裂情况的出现

  • 如果只有两个节点,需要移除一个节点,在Leader在发起命令之后,另一个节点挂了,这时候系统没法恢复了。

日志压缩

为了避免日志一直不断地增长,Raft使用了Snapshot机制,Snapshot之前地日志都可以丢弃。
Raft算法为了解决Snapshot地性能问题,给出了三个解决方案:

  1. 不要做太频繁,否则消耗磁盘带宽。

  2. 不要做的太不频繁,否则一旦节点重启需要回放大量日志,影响可用性。系统推荐当日志达到某个固定的大小做一次snapshot。

  3. 做一次snapshot可能耗时过长,会影响正常log entry的replicate。这个可以通过使用copy-on-write的技术来避免snapshot过程影响正常log entry的replicate。

简短地结束

这篇导读简单地概括了Raft算法,但是在Raft论文中更加详细地提到了性能优化、Raft正确性证明和选举地详细过程,甚至给出伪代码等等,同时TiDB、MongoDB等分布式数据库都采用了Raft算法,并且在此之上做了大量地优化,同样Raft算法也拥有了大量地开源实现库,具体地可以参考官网继续学习。

参考文章

https://raft.github.io/raft.pdf
http://www.infoq.com/cn/articles/raft-paper
https://www.jianshu.com/p/138b4d267084
https://www.jianshu.com/p/99562bfec5c2
https://zhuanlan.zhihu.com/p/27910576

以上是关于Raft算法导读的主要内容,如果未能解决你的问题,请参考以下文章

图解:什么是Raft算法?

深入解析Raft模块在ZNBase中的优化改造(下)

深入解析Raft模块在ZNBase中的优化改造(上)

Raft算法

分布式共识算法——Raft算法(图解)

分布式共识算法——Raft算法(图解)