文章目录
一句话简介ratf算法
没听过不要紧,我原先也没听过。
Raft 算法是通过一切以领导者为准的方式,实现一系列值的共识和各节点日志的一致。
熟悉吗?redis的哨兵用的就是这一套,不过哨兵简化了一些部分,提升了运行效率,降低了一致性,保证了最终一致性。
晕吗?晕就完蛋了。
分布式一致性的难点
1、时钟不一致
如果两条命令,分别为 A、B,被发往两个节点。由于网络延迟等原因,导致一个节点先收到A,再收到B;另一个节点先收到B,再收到A,这时候就有可能会造成数据的不一致性。
在一个节点上,可以通过时间区分操作的先后顺序,但是在多个节点上,由于物理时钟难以同步,所以我们无法直接再分布式系统中直接使用物理时钟区分事件时序。
2、网络不可靠
可能一个节点根本就没有收到命令B呢?
3、节点崩溃
CAP理论
分区容错性:正常情况下,一个集群内所有的节点网络应该是互通的,但由于网络不可靠,可能导致一些节点之间网络不通,整个网络被分为几块区域。出现网络不通后,其他不连通的分区就访问不到这个节点的数据了(如果是独苗就坏事儿了)。通常将数据复制到集群的所有节点,保证及时出现网络分区后,不同网络分区都可以访问到数据。
一致性:集群中所有节点的数据保持一致。
可用性:集群一致处于可用状态。
传说这三者几乎不可兼得。如果要分区容错性,很难保证一致性,具体原因往上看。如果要一致性,又要分区一致性,那就是数据不一致就直接关掉服务咯。所以三者几乎不可兼得。
所以现在用的比较多的是:最终一致性。
就是在满足分区容错的情况下,允许在同步的过程中数据偶尔出现不一致,只要同步完之后每个节点的数据保持一致就可以。
分布式一致性:Raft算法
共识算法就是保证一个集群的多台机器协同工作,在遇到请求时,数据能够保持一致。即使遇到机器宕机,整个系统仍然能够对外保持服务的可用性。
Raft将共识问题分解三个子问题:
Leader election 领导选举:有且仅有一个leader节点,如果leader宕机,通过选举机制选出新的leader;
Log replication 日志复制:leader从客户端接收数据更新/删除请求,然后日志复制到follower节点,从而保证集群数据的一致性;
Safety 安全性:通过安全性原则来处理一些特殊case,保证Raft算法的完备性;
所以,Raft算法核心流程可以归纳为:
首先选出leader,leader节点负责接收外部的数据更新/删除请求;
然后日志复制到其他follower节点,同时通过安全性的准则来保证整个日志复制的一致性;
如果遇到leader故障,followers会重新发起选举出新的leader;
这里先介绍一下日志同步的概念:服务器接收客户的数据更新/删除请求,这些请求会落地为命令日志。只要输入状态机的日志命令相同,状态机的执行结果就相同。所以Raft的核心就是leader发出日志同步请求,follower接收并同步日志,最终保证整个集群的日志一致性。
选举过程
当我们启动一个新的Raft集群或某个领导者不可用时,将通过集群中所有成员节点之间协商来选举一个新的领导者。
Raft使用基于心跳的RPC机制来检测何时开始新的选举。 在正常期间,Leader会定期向所有可用的Follower发送心跳消息(实际中可能把日志和心跳一起发过去)。 因此,其他节点以Follower状态启动,只要它从当前Leader那里收到周期性的心跳,就一直保持在Follower状态。
如果追随者在一段时间内未收到心跳消息,就会触发选举超时,该选举超时将启动新的选举以选择领导者。
当Follower达到其超时时间时,它将通过以下方式启动选举程序:
增加当前Term,为自己投票
并将“ RequestVote” RPC发送给集群中的所有其他人,这时也就是从Follower转换为Candidate
根据Candidate从集群中其他节点收到的响应,可以得出选举的三个结果。
1、如果大多数节点以“是”投票支持RequestVote请求,则候选人S1赢得选举。
2、在S1等待期间,它可能会从另一个声称是领导者的节点接收AppendEntries RPC。 如果S1的候选Term低于AppendEntries RPC的接收Term,则候选S1放弃并接受另一个节点作为合法领导者。
3、拆分投票方案:当有多个Follower同时成为Candidate时,任何候选人都无法获得多数。 这被称为分裂投票情况。 在这种情况下,每个Candidate都将超时,并且将触发新的选举。
为了最大程度地减少拆分投票的情况,Raft使用了随机选举超时机制,该机制将随机超时值分配给每个节点。
新官上任
一旦节点成为Leader,它就可以从客户端接收命令/日志条目。 使用AppendEntries RPC发送日志条目。
从客户端收到命令后,Leader将为命令分配Term和日志索引Index。 然后,Leader尝试在集群中的大多数节点上执行复制命令。 如果复制成功,则将命令提交给集群,并将响应发送回客户端。
Leader将命令附加到日志,并使用该命令广播AppendEntries RPC。
每个节点都在本地申请条目并成功答复。当大多数跟随者节点已在本地成功提交日志条目时,Leader将提交(前一阶段相当于Try,接下来是Commit,类似两阶段提交协议)命令并将成功响应发送回客户端。当领导者提交日志条目时,它还会更新提交索引,并且下一条AppendEntries广播消息会将更新的提交索引复制到所有跟随者节点。当领导者提交一个条目时,它还将在当前日志索引之前提交所有全部内容。
如果某些节点不可用于接收日志条目,或者消息在运行中丢失,则日志中可能存在不一致之处。Leader负责调和此类不一致。 当Follower收到AppendEntries RPC时,它也包含Term和上一个日志条目的日志索引。 它与Follower日志的最后一个条目不匹配,那么Follower将发送失败的响应。 因此,Leader知道该特定节点的日志中存在不一致之处。
领导者通过跟踪每个Follower的nextIndex来解决日志不一致问题。 当给定的Follower的日志不一致时(即,如果发送的响应失败),则Leader递减nextIndex值,然后重试AppendEntries RPC。 此过程将继续进行,直到Follower日志与领导者一致为止。 此外,每个节点还将本地日志保存在持久存储中。
根正苗红
当前的Leader election 领导选举和Log replication 日志复制并不能保证Raft算法的安全性,在一些特殊情况下,可能导致数据不一致,所以需要引入下面安全性规则。
(1)Election Safety 选举安全性:避免脑裂问题
选举安全性要求一个任期Term内只能有一个leader,即不能出现脑裂现象,否者raft的日志复制原则很可能出现数据覆盖丢失的问题。Raft算法通过规定若干投票原则来解决这个问题:
一个任期内,follower只会投票一次票,且先来先得;
Candidate存储的日志至少要和follower一样新;
只有获得超过半数投票才有机会成为leader;
(2)Leader Append-Only 日志只能由leader添加修改
Raft算法规定,所有的数据请求都要交给leader节点处理,要求:
leader只能日志追加日志,不能覆盖日志;
只有leader的日志项才能被提交,follower不能接收写请求和提交日志;
只有已经提交的日志项,才能被应用到状态机中;
选举时限制新leader日志包含所有已提交日志项;
(3)Log Matching 日志匹配特性
这点主要是为了保证日志的唯一性,要求:
如果在不同日志中的两个条目有着相同索引和任期号,则所存储的命令是相同的;
如果在不同日志中的两个条目有着相同索引和任期号,则它们之间所有条目完全一样;
(4)Leader Completeness 选举完备性:leader必须具备最新提交日志
Raft规定:只有拥有最新提交日志的follower节点才有资格成为leader节点。具体做法:candidate竞选投票时会携带最新提交日志,follower会用自己的日志和candidate做比较。
如果follower的更新,那么拒绝这次投票;
否则根据前面的投票规则处理。这样就可以保证只有最新提交节点成为leader;
因为日志提交需要超过半数的节点同意,所以针对日志同步落后的follower(还未同步完全部日志,导致落后于其他节点)在竞选leader的时候,肯定拿不到超过半数的票,也只有那些完成同步的才有可能获取超过半数的票成为leader。
日志更新判断方式是比较日志项的term和index:
如果TermId不同,选择TermId最大的;
如果TermId相同,选择Index最大的;
(5)State Machine Safety 状态机安全性:确保当前任期日志提交
Raft对日志提交有额外安全机制:leader只能提交当前任期Term的日志,旧任期Term(以前的数据)只能通过当前任期Term的数据提交来间接完成提交。简单的说,日志提交有两个条件需要满足:
当前任期;
复制结点超过半数;
仍需努力
1、Raft严格是单个Leader协议,而且太多的流量会阻塞系统,存在解决此瓶颈的Paxos算法的某些变体。
2、有许多假设在起作用,降低了现实生活中的适用性。
3、K + 1的复制服务器可以容忍K服务器中的关闭/故障。