浅析分布式一致性算法 - Raft算法:定义、为什么需要一致性、强/弱一致性分类区别、raft三种状态、领导选举算法流程、日志复制流程、安全选举限制、如何解决split brain的问题
一致性算法的定义:一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。
一、为什么需要一致性
1、数据不能存在单个节点(主机)上,否则可能出现单点故障。
2、多个节点(主机)需要保证具有相同的数据。
3、一致性算法就是为了解决上面两个问题。
二、一致性分类
1、强一致性
说明:保证系统改变提交以后立即改变集群的状态。
模型:Paxos、Raft(muti-paxos)、ZAB(muti-paxos)
2、弱一致性
说明:也叫最终一致性,系统不保证改变提交以后立即改变集群的状态,但是随着时间的推移最终状态是一致的。
模型:DNS系统、Gossip协议
三、更加直观的 Raft 算法
Raft 是用于一个管理日志一致性的协议,相比于 Paxos 协议 Raft 更易于理解和去实现它。为了提高理解性,Raft 将一致性算法分为了几个部分,包括领导选取(leader selection)、日志复制(log replication)、安全(safety),并且使用了更强的一致性来减少了必须需要考虑的状态。
1、解决什么问题
分布式存储系统通常通过维护多个副本来提高系统的availability,带来的代价就是分布式存储系统的核心问题之一:维护多个副本的一致性。
Raft 协议基于复制状态机(replicated state machine),即一组server从相同的初始状态起,按相同的顺序执行相同的命令,最终会达到一致的状态,一组server记录相同的操作日志,并以相同的顺序应用到状态机。
Raft 有一个明确的场景,就是管理复制日志的一致性。如图,每台机器保存一份日志,日志来自于客户端的请求,包含一系列的命令,状态机会按顺序执行这些命令。一致性算法管理来自客户端状态命令的复制日志,保证状态机处理的日志中的命令的顺序都是一致的,因此会得到相同的执行结果。
2、Raft 状态
相比 Paxos,Raft 算法理解起来直观的很。Raft算法将Server划分为3种状态,或者也可以称作角色。一个 Raft 集群包含若干个服务器节点;通常是 5 个,这允许整个系统容忍 2 个节点的失效,每个节点处于以下三种状态之一:
follower(跟随者)
:所有节点都以follower
的状态开始。如果没收到leader
消息则会变成candidate
状态 —— 被动响应请求RPC,从不主动发起请求RPC。candidate(候选人)
:会向其他节点“拉选票”,如果得到大部分的票则成为leader
。这个过程就叫做Leader选举(Leader Election) —— 一种临时的角色,只存在于leader的选举阶段,某个节点想要变成leader,那么就发起投票请求,同时自己变成candidate。如果选举成功,则变为candidate,否则退回为followerleader(领导者)
:所有对系统的修改都会先经过leader —— 负责Client交互和log复制,同一时刻系统中最多存在1个
状态或者说角色的流转如下:
在 Raft 中,问题分解为:领导选取、日志复制、安全和成员变化。复制状态机通过复制日志来实现:
- 日志:每台机器保存一份日志,日志来自于客户端的请求,包含一系列的命令
- 状态机:状态机会按顺序执行这些命令
- 一致性模型:分布式环境下,保证多机的日志是一致的,这样回放到状态机中的状态是一致的
3、Raft 一致性算法
Raft 通过选出一个leader来简化日志副本的管理,例如:日志项(log entry)只允许从leader流向follower。
基于leader的方法,Raft算法可以分解成三个子问题:
(1)Leader election
(领导选举):原来的leader挂掉后,必须选出一个新的leader
(2)Log replication
(日志复制):leader从客户端接收日志,并复制到整个集群中
(3)Safety
(安全性):如果有任意的server将日志项回放到状态机中了,那么其他的server只会回放相同的日志项
四、Leader election (领导选举) —— Raft 算法流程
Raft 使用一种心跳机制来触发领导人选举。
当服务器程序启动时,他们都是 follower
(跟随者) 身份。如果一个跟随者在一段时间里没有接收到任何消息,也就是选举超时,然后他就会认为系统中没有可用的领导者,然后开始进行选举以选出新的领导者。
要开始一次选举过程,follower
会给当前term加1并且转换成candidate
状态。
然后他会并行的向集群中的其他服务器节点发送请求投票的 RPCs 来给自己投票。
候选人的状态维持直到发生以下任何一个条件发生的时候:
-
他自己赢得了这次的选举
- 如果这个节点赢得了半数以上的vote就会成为leader,每个节点会按照first-come-first-served的原则进行投票,并且一个term中只能投给一个节点, 这样就保证了一个term最多有一个节点赢得半数以上的vote。
- 当一个节点赢得选举, 他会成为leader, 并且给所有节点发送这个信息, 这样所有节点都会回退成follower。
-
其他的服务器成为领导者
如果在等待选举期间,candidate接收到其他server要成为leader的RPC,分两种情况处理:
- 如果leader的term大于或等于自身的term,那么改
candidate
会转成follower
状态 - 如果leader的term小于自身的term,那么会拒绝该
leader
,并继续保持candidate
状态
- 如果leader的term大于或等于自身的term,那么改
-
一段时间之后没有任何一个获胜的人
-
有可能,很多follower同时变成candidate,导致没有candidate能获得大多数的选举,从而导致无法选出主。当这个情况发生时,每个candidate会超时,然后重新发增加term,发起新一轮选举RPC。需要注意的是,如果没有特别处理,可能出导致无限地重复选主的情况。
-
Raft采用随机定时器的方法来避免上述情况,每个candidate选择一个时间间隔内的随机值,例如150-300ms,采用这种机制,一般只有一个server会进入candidate状态,然后获得大多数server的选举,最后成为主。每个candidate在收到leader的心跳信息后会重启定时器,从而避免在leader正常工作时,会发生选举的情况。
-
有些概念我们需要理解下:
1、Term
Term的概念类比中国历史上的朝代更替,Raft 算法将时间划分成为任意不同长度的任期(term)。任期用连续的数字进行表示。每一个任期的开始都是一次选举(election),一个或多个候选人会试图成为领导人。如果一个候选人赢得了选举,它就会在该任期的剩余时间担任领导人。在某些情况下,选票会被瓜分,有可能没有选出领导人,那么,将会开始另一个任期,并且立刻开始下一次选举。Raft 算法保证在给定的一个任期最多只有一个领导人。
2、RPC
Raft 算法中服务器节点之间通信使用远程过程调用(RPCs),并且基本的一致性算法只需要两种类型的 RPCs,为了在服务器之间传输快照增加了第三种 RPC。RPC有三种:
- RequestVote RPC:候选人在选举期间发起
- AppendEntries RPC:领导人发起的一种心跳机制,复制日志也在该命令中完成
- InstallSnapshot RPC: 领导者使用该RPC来发送快照给太落后的追随者
3、选举流程
(1)follower增加当前的term,转变为candidate。
(2)candidate投票给自己,并发送RequestVote RPC给集群中的其他服务器。
(3)收到RequestVote的服务器,在同一term中只会按照先到先得投票给至多一个candidate。且只会投票给log至少和自身一样新的candidate。
candidate节点保持(2)的状态,直到下面三种情况中的一种发生。
- 该节点赢得选举。即收到大多数的节点的投票。则其转变为leader状态。
- 另一个服务器成为了leader。即收到了leader的合法心跳包(term值等于或大于当前自身term值)。则其转变为follower状态。
- 一段时间后依然没有胜者。该种情况下会开启新一轮的选举。
Raft中使用随机选举超时时间来解决当票数相同无法确定leader的问题。
五、Log replication (日志复制)
日志复制(Log Replication)主要作用是用于保证节点的一致性,这阶段所做的操作也是为了保证一致性与高可用性。当Leader选举出来后便开始负责客户端的请求,所有事务(更新操作)请求都必须先经过Leader处理,日志复制(Log Replication)就是为了保证执行相同的操作序列所做的工作。
在Raft中当接收到客户端的日志(事务请求)后先把该日志追加到本地的Log中,然后通过heartbeat把该Entry同步给其他Follower,Follower接收到日志后记录日志然后向Leader发送ACK,当Leader收到大多数(n/2+1)Follower的ACK信息后将该日志设置为已提交并追加到本地磁盘中,通知客户端并在下个heartbeat中Leader将通知所有的Follower将该日志存储在自己的本地磁盘中。
当选出 leader
后,它会开始接受客户端请求,每个请求会带有一个指令,可以被回放到状态机中。leader
把指令追加成一个log entry
,然后通过AppendEntries RPC并行的发送给其他的server,当该entry被多数派server复制后,leader
会把该entry回放到状态机中,然后把结果返回给客户端。
当 follower
宕机或者运行较慢时,leader
会无限地重发AppendEntries给这些follower,直到所有的follower都复制了该log entry。
Raft 的log replication保证以下性质(Log Matching Property):
-
如果两个 log entry 有相同的 index 和 term,那么它们存储相同的指令
-
如果两个 log entry 在两份不同的日志中,并且有相同的 index 和 term,那么它们之前的log entry是完全相同的
其中特性一通过以下保证:
- leader在一个特定的term和index下,只会创建一个log entry
- log entry不会改变它们在日志中的位置
特性二通过以下保证:
- AppendEntries会做log entry的一致性检查,当发送一个AppendEntriesRPC时,leader会带上需要复制的log entry前一个log entry的(index, iterm)
如果follower没有发现与它一样的log entry,那么它会拒绝接受新的log entry 这样就能保证特性二得以满足。
六、安全性 —— 选举限制
在一些一致性算法中,即使一台server没有包含所有之前已提交的log entry,也能被选为主,这些算法需要把leader上缺失的日志从其他的server拷贝到leader上,这种方法会导致额外的复杂度。
相对而言,raft使用一种更简单的方法,即它保证所有已提交的log entry都会在当前选举的leader上,因此,在raft算法中,日志只会从leader流向follower。
为了实现上述目标,raft在选举中会保证,一个candidate只有得到大多数的server的选票之后,才能被选为主。得到大多数的选票表明,选举它的server中至少有一个server是拥有所有已经提交的log entry的,而leader的日志至少和follower的一样新,这样就保证了leader肯定有所有已提交的log entry。
七、时间和可用性
领导人选举是 Raft 中对时间要求最为关键的方面。Raft 可以选举并维持一个稳定的领导人,只要系统满足下面的时间要求:广播时间(broadcastTime) << 选举超时时间(electionTimeout) << 平均故障间隔时间(MTBF)
-
广播时间指的是从一个服务器并行的发送 RPCs 给集群中的其他服务器并接收响应的平均时间;
-
选举超时时间就是选举的超时时间限制
-
平均故障间隔时间就是对于一台服务器而言,两次故障之间的平均时间。
选举超时时间要大于广播时间的原因是,防止跟随者因为还没收到领导者的心跳,而重新选主。
选举超时时间要小于MTBF的原因是,防止选举时,能正常工作的server没有达到大多数。
对于广播时间,一般在[0.5ms,20ms]之间,而平均故障间隔时间一般非常大,至少是按照月为单位。因此,一般选举超时时间一般选择范围为[10ms,500ms]。因此,当领导者挂掉后,能在较短时间内重新选主。
八、如何解决 split brain 问题
分布式协议一个著名问题就是 split brain 问题。
简单说,就是比如当你的 cluster 里面有两个结点,它们都知道在这个 cluster 里需要选举出一个 master。那么当它们两之间的通信完全没有问题的时候,就会达成共识,选出其中一个作为 master。但是如果它们之间的通信出了问题,那么两个结点都会觉得现在没有 master,所以每个都把自己选举成 master。于是 cluster 里面就会有两个 master。
区块链的分叉其实类似分布式系统的split brain。
一般来说,Zookeeper会默认设置:
- zookeeper cluster的节点数目必须是奇数。
- zookeeper 集群中必须超过半数节点(Majority)可用,整个集群才能对外可用。
Majority 就是一种 Qunroms 的方式来支持Leader选举,可以防止 split brain出现。奇数个节点可以在相同容错能力的情况下节省资源。
参考文章:https://www.cnblogs.com/binyue/p/8647733.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2019-07-09 web安全总结
2019-07-09 小知识随手记(八):颜色转换、const常量的改动内因、js消息队列、JSON.stringify()的一些特性、多背景图写法、懒加载原理、document.body与document.documentElement区别及使用、移动ios卡顿问题、移位运算符、nextSibling 和nextElementSibling的区别
2018-07-09 CSS阻塞渲染、怎么防止css阻塞
2018-07-09 网页性能优化:防止JavaScript、CSS阻塞浏览器渲染页面
2017-07-09 解决微信分享报错config:fail,invalid signature的问题、微信网页开发invalid signature/permission denied/offline verifying等错误处理
2017-07-09 引用传递与值传递
2017-07-09 CSS深入了解border:利用border画三角形等图形