理解分布式一致性与Raft算法

理解分布式一致性与Raft算法

永远绕不开的CAP定理

出于可用性及负载方面考虑,一个分布式系统中数据必然不会只存在于一台机器,一致性简单地说就是分布式系统中的各个部分保持数据一致

但让数据保持一致往往并不像看上去那么简单,假设我们有两台机器A与B,这时A更新了数据,A需要将更新的指令同步到B,如果A到B网络传输到B数据落地的总时间为500ms,那么这个500ms就是可能造成数据不一致的时间窗口,假如两台机器分属不同机房,甚至分属不同国家的机房,其时间窗口会更大,具体会造成什么影响呢?

举个栗子??,假如用户a进行转账操作,节点A更新了数据,他在转账后显示余额为0,但他刷新一下页面请求到了节点B发现自己的余额又回到了原来的余额,然而这只是显示不一致,但假设他又在节点B上进行在进行了转账操作,转账中的余额校验也依旧访问的是节点B,那么可能会造成的影响不言而喻。

CAP定理

在谈分布式一致性之前,我们首先了解一个定理,那就是CAP定理,请注意,CAP是定理而非理论,CAP定理证明了一个分布式系统只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。

  • 一致性(Consistency):指的就是整个集群的所有节点数据保持一致
  • 可用性(Availability):在数据同步过程中,集群是否是可用状态
  • 分区容忍性(Partition tolerance):是否能够容忍网络分区的发生

C和A相对好理解,这里着重说一下P(Partition tolerance)分区容忍性,听着比较拗口,说实话,刚开始看到他的时候也是一脸茫然,分区?什么是分区?其实分区(Partition)简单的说就是服务器之间的网络通信断了,两边的服务器变成两个独立的集群,这就是所谓的分区,断了的原因有很多比如交换机故障,路由器故障,扫地阿姨把网线拔了等等,然后什么是容忍性(tolerance),这个就很好理解了,是不是发生分区了我的服务就不再提供服务了呢,当然不是,否则也就没有高可用一说了,那么我们能否说不做网络故障可能发生的假设呢,答案必然是不能的,首先网络延迟是必然的,网络延迟的过程中也可以将其当做发生分区,另外网络故障也可以说是必然的,详见墨菲定律(滑稽脸)

其实P也不是完全不能抛弃的,很简单,我们干掉网线,只保留一台单机数据库,就只有一个区,何来分区一说,对啊,所以说我们常见的传统单机数据库(RDBMS)就是可以满足CA的,如:MySQL,Oracle等等,当然,前提是你没做主从之类的分布式方案。由此可见,在所有分布式系统中P几乎都是不可抛弃的,所以说我们的选择也就只剩两个了AP和CP。

如何理解CP与AP?

举个简单例子,若我们集群有两台机器,而两台机器网络发生中断而导致出现分区:

  • 如果我们在双发无法通信的情况下继续允许两边进行写入,则必然造成数据的不一致,这时我们实现了AP而抛弃了C。
  • 但如果我们禁止其中一方进行写入,这样就可以保证系统的一致性了,但我们却因为将一中一个副本置为不可用而导致了A属性的丧失,也是说实现了CP。

这样CAP理论是否变得好理解了一些?当然现在对于CAP理论的争议也很大,但并不是怀疑CAP定理是否能被证伪,而是说CAP理论也许并不适用于我们通常对数据库系统的描述,有时我们并不能简单的将数据库划分为AP或CP。举个栗子??,如果我们有一个single master+multiple slaves的mysql数据库,当leader不可用时,用户则不能进行写入,也就丧失了A属性,但由于MySQL是通过binlog异步同步数据库的,用户也有可能读到的是旧数据,所以说该系统也许既不满足A也不满足C,仅仅满足了P属性。

(关于CAP的争议讨论推荐阅读:《请不要再称数据库是CP或者AP (Please stop calling databases CP or AP)》

线性一致性与一致性级别

基于CAP理论的AP与CP互斥的原则,针对C的取舍,我们简单划分成了3个级别来描述(特殊场景下会更多):

强一致性(线性一致性)

强一致性可以理解为当写入操作完成后,任何客户端去访问任何存储节点的值都是最新的值,将分布式的一致性过程对客户端透明,客户端操作一个强一致性的数据库时感觉自己操作的是一个单机数据库,强一致性就是CAP定理中所描述的C(Consistency),同时下面的讲解的Raft算法就是实现线性一致性的一直

弱一致性

弱一致性是与强一致性对立的一种一致性级别,弱一致性简单地说不去对一致性进行保证,客户端在写入成功后依旧可能会得到旧的值,这也就是舍弃C可能造成的问题,但某些系统下,对一致性的要求并不高,从而可以舍弃强一致策略可能带来的性能与可用性消耗。

最终一致性

最终一致性也可以理解成弱一致性的一种,使用这种一致性级别,依旧可能在写入后读到旧值,但做出的改进是要求数据在有限的时间窗口内最终达到一致的状态。也就说就算现在不一致,也早晚会达到一致,但狭义上的弱一致性并不对一致性做出任何保证,也许某些节点永远不会达到一致,其实最终一致性的核心就是保证同步的请求不会丢失,在请求到达时节点的状态变为最新状态,而不考虑请求传输时的不一致窗口,DNS就是典型的最终一致性系统。

Raft算法

Raft算法的论文题目是《In Search of an Understandable Consensus Algorithm (Extended Version)》(《寻找一种易于理解的一致性算法(扩展版)》),很容易理解,Raft算法的初衷就是设计一个相较于Paxos更易于理解的强一致性算法,虽说更好理解,但依旧毕竟是分布式一致性算法,其算法复杂程度及各种状态的多样性依旧需要较高的理解成本。但是花时间成本去学习Raft是值得的,理解Raft后能够很大程度加深你对分布式及线性一致性的理解,这次仅是基于个人理解的描述性介绍Raft算法,不对如选举异常或宕机等情况的处理做更多探究,如果有什么疑问欢迎进行讨论,同时感兴趣的同学也推荐阅读Raft原版论文(中文版):寻找一种易于理解的一致性算法(扩展版)

Raft算法作为单纯的一致性算法,使用场景并非仅仅在数据库,Raft算法将分布式一致性问题拆分为若干个子问题进行解决,其他的相关机制均是这三个子问题的延伸,接下面我们详细阐述一下这三个子问题。
注:下面所涉及到所有RPC的协议字段都可以在论文中找到

Leader选举:避免多节点状态竞争

通常Raft节点拥有5个节点,将这五个节点分为三种角色Leader,Follower和Candidate,所有节点只可能是这三种角色,并且所有节点的对等的,它们都可以成为这三种角色的其中之一。
其实虽然有三种角色,但进行抽象以后可以理解为集群只有Leader与Follower两种角色,Candidate是Leader的预备役而已。
简单的讲可以将Raft系统理解为一个一主多从(single master multiple slaves)的RDBMS(MySQL等),但RDBMS通常采用的方案是Master节点用于写,而Followers用于读,但Raft不同的是不管是读和写都要经由Master节点分发给Followers节点,这样做的缺点是可能会导致Leader节点的负载会高于Followers,但这样做的好处是实现了强一致性,强一致性的部分下面会详细说明。
既然是单Leader,那么假设我们的Leader宕机了怎么办,这时候我们就可以将这个问题拆分为两个部分:

  1. 如何发现Leader节点宕机
  2. 如果发现Leader节点宕机如何重新选举Leader

第一个问题在Raft中的解决方案是增加心跳(heartbeat),Leader定期向所有Followers发送心跳消息,若follower在一段时间内没有收到leader的选票(心跳超时,超时时间随机,由Follower自己控制),则认为Leader宕机,开始选举
第二个问题引申出了三个概念

  1. 任期号(term)
  2. 选票(vote)
  3. 候选人(candidate)

当Follower认定Leader宕机后,他会自告奋勇的认为自己应该成为新一轮的Leader,这时他会将自己的状态转换为candidate,并向Node广播请求选票的RPC,如果超过半数的Node同意他成为新的Leader则代表他应得了这次选举,他就会变成新的Leader。
我们来简单说几个异常的情况及Raft的处理方式:

  • 虽然心跳超时随机的策略大幅度减少了两个Followers同时超时的情况,但依旧不能保证不会出现两个candidates同时超时的情况,假如说出现两个candidate同时超时的情况就有可能接连发生两个candidates同事发起选举投票,两个candidates将选票瓜分,最终没有人能够获得超过半数的选票,这个异常机制的保障措施是candidates当角色发生转换后candidates会重置超时时间,如若一段时间内没有新的Leader产生,则重新发起新一轮的选举,因为所有超时时间均是随机的,所以第二次发生瓜分选票的可能性已经变得微乎其微。
    在论文中也验证了小幅度的随机既可以让选票瓜分的情况大大减小:
只需要在选举超时时间上使用很少的随机化就可以大大避免选票被瓜分的情况。在没有随机化的情况下,在我们的测试里,选举过程往往都需要花费超过 10 秒钟由于太多的选票瓜分的情况。仅仅增加 5 毫秒的随机化时间,就大大的改善了选举过程,现在平均的宕机时间只有 287 毫秒。增加更多的随机化时间可以大大改善最坏情况:通过增加 50 毫秒的随机化时间,最坏的完成情况(1000 次尝试)只要 513 毫秒。
  • 如果Candidate如果接收到其他Candidate的的选举请求的话,如何认定究竟是继续收集选票还是投票给其他candidate?如果大家一直互相谦让或者一直互相竞争,岂不是最终谁也不能够成为Leader了吗?
    针对这个该问题,在请求选举的RPC中Raft增加了任期号(term)的概念,在raft系统初始化时,所有node的term均为0,当某一个节点成为candidate时,该节点就会将term+1并发起选举,follower仅会投票给RPC中的term大于等于自己的currenttTerm的RPC投票,这样就避免了无限对等投票的可能性。Raft协议引入了任期号(term)的概念,任期号很简单,所有node节点在初始化的时候,选票号都为1,当任何follower当选candidate的时候都会将选票号置加1,并附带至发起选举的RPC中,如若其他节点收到了选举投票RPC,他会比对自身的选举号是否比RPC中的选举号小,如果小于RPC中的选举号,则他会承认对方的权威

日志复制:实现指令序列化,是实现强一致性的根本

只有leader节点可以和客户端通信,同时将log复制至所有follower,强制folloer与leader的log保持一致。
所谓日志,其实就可以理解为增删改查(CRUD)的命令(但其实raft并不关心这个日志是做什么的),这个命令在这里称为log,raft将log序列化,log依次复制并执行到每一个node,就能实现节点的强一致性,raft中解释是“如果有任何的服务器节点已经应用了一个确定的日志条目到它的状态机中,那么其他服务器节点不能在同一个日志索引位置应用一个不同的指令”,也可以理解为raft日志复制的安全性保证是确保所有序列按顺序append,不能越过某一个log,这样就保证了假如查询发生在修改之后(但可能有不同的实现方式),那最终不论访问的是哪一个节点,查询必然发生在修改之后,这样就可以确保拿到的数据是最新的了。
具体实现分为若干步:

  1. leader接收到客户端发来的command
  2. leader将当前的index+1,赋给该log,并append到自己的Logs中
  3. leader广播(RPC)该log给所有follower
  4. 当超过半数的follower回应接收成功时,leader就将该log commit,并通知所有follower commit

安全性:保证不同的状态机以相同的顺序执行相同的指令

上面提到了Raft算法是如何进行leader选举和如何进行日志复制的,至此其实已经可以实现分布式一致性了,但是如果想保证日志提交精准无误则需要更多地安全性保障措施

1.选举限制

假如某个candidate在选举成为leader时没有包含所有的已提交日志,这时就会出现日志顺序不一致的情况,在其他一致性算法中会在选举完成后进行补漏,但这大大增加了复杂性。而Raft则采用了一种简单的方式避免了这种情况的发生

  1. Raft在RequestVote RPC(候选人请求成为Leader的RPC请求) 中增加了自己的日志信息
  2. 当followers收到RPC请求时则会把candidate的日志信息与自己的日志信息进行比较
  3. 假如follower的日志信息相较于candidate要更新,则拒绝这个选票,反之则同意该candidate成为leader

经过这一系列的步骤,则保证了仅允许包含了所有已提交日志的candidate赢得选举成为候选人,从而也就避免了leader缺少已提交日志的情况了

2.延迟提交

上面提到过,我们进行日志提交需要三个阶段:

  1. leader将log复制到大多数followers
  2. follower将日志复制,并告诉leader自己已经复制成功
  3. leader收到了大多数followers的复制成功响应,并提交日志

但是这里有一个问题,假如leader已经将日志复制到了大多数followers,但却在提交之前崩溃了,虽然raft算法中规定后续的leader应该继续完成之前的复制任务,但在下图的情况下依旧会出现已经复制到大多数节点的log依旧被覆盖掉了。

在 (a) 中,S1 是领导者,部分的复制了索引位置 2 的日志条目。
在 (b) 中,S1 崩溃了,然后 S5 在任期 3 里通过 S3、S4 和自己的选票赢得选举,然后从客户端接收了一条不一样的日志条目放在了索引 2 处。
然后到 (c),S5 又崩溃了;S1 重新启动,选举成功,开始复制日志。在这时,来自任期 2 的那条日志已经被复制到了集群中的大多数机器上,但是还没有被提交。
如果 S1 在 (d) 中又崩溃了,S5 可以重新被选举成功(通过来自 S2,S3 和 S4 的选票),然后覆盖了他们在索引 2 处的日志

Raft为了避免这种情况发生,而规定了一个原则,Raft 永远不会通过计算副本数目的方式去提交一个之前任期内的日志条目,也就是说假如这个log的任期已经过了,就算是已经复制到了大多数节点,Raft也不会去提交它,那么在这种情况下如何对之前任期的log进行提交呢,这时引入了Raft的Log Matching(日志匹配原则),该原则的描述是“如果两个日志在相同的索引位置上的日志条目的任期号相同, 那么我们就认为日志从头到这个索引位置之间的条目完全相同”,这个机制的原理是leader在进行日志复制时会检查上一条日志是否一致,如果不一致则会将上一条复制给follower,在复制上一条的过程中依旧会进行检查,这样一个递归的过程保证了Raft的Log Matching原则。

原文地址:https://www.cnblogs.com/mokafamily/p/11303534.html

时间: 2024-11-09 23:53:19

理解分布式一致性与Raft算法的相关文章

各大中间件底层技术-分布式一致性协议 Raft 详解

前言 正式介绍 Raft 协议之前,我们先来举个职场产研团队的一个例子??. 方式一: 在一个技术团队内假设角色都是 均等的,会导致什么情况呢?产品提出一个需求,就可以随便去找团队中的任意一个人去发起需求.如果这个人因为请假走了,但是他没有把需求及时同步给团队其他人,因此会导致该需求存在很大的延迟. 方式二: 在技术团队中选举一个 ** Leader角色**,产品提出的需求必须优先提给 Leader,找 Leader 先沟通.Leader 自己消化完后,在将需求传达给团队其他成员.如果 Lead

分布式一致性哈希算法

一致性哈希算法是一种分布式哈希算法,主要是为了解决互联网中的热点(Hot spot)问题 计算公式 hash(服务器IP地址) % 2^32 hash(对象) % 2^32 将对象Hash后的值映射到顺时针最近的一台服务器上 Java实现 package com.bounter.mybatis.util; import java.util.LinkedList; import java.util.List; import java.util.SortedMap; import java.util

分布式系统理论进阶 - Raft、Zab

引言 <分布式系统理论进阶 - Paxos>介绍了一致性协议Paxos,今天我们来学习另外两个常见的一致性协议——Raft和Zab.通过与Paxos对比,了解Raft和Zab的核心思想.加深对一致性协议的认识. Raft Paxos偏向于理论.对如何应用到工程实践提及较少.理解的难度加上现实的骨感,在生产环境中基于Paxos实现一个正确的分布式系统非常难[1]: There are significant gaps between the description of the Paxos al

【转载】Paxos以及分布式一致性的学习

开始搜出来这篇文章(link),发现不知所云,先忽略. 然后搜出来这篇文章(link),说是偏向工程实现,建议先看维基(link),但是维基打不开. 所以还是先看知乎的这篇文章吧(https://www.zhihu.com/question/19787937/answer/82340987) Lamport用两段话就描述清楚了它的流程,他老人家也说paxos其实是个简单的算法.但是是我在工程领域见过最为精妙的算法. 分布式一致性是个有趣的领域,而Paxos和类似的协议对这个问题的重要性不喻,在过

Raft 为什么是更易理解的分布式一致性算法

Raft 为什么是更易理解的分布式一致性算法 一致性问题可以算是分布式领域的一个圣殿级问题了,关于它的研究可以回溯到几十年前. 拜占庭将军问题 Leslie Lamport 在三十多年前发表的论文<拜占庭将军问题>(参考[1]). 拜占庭位于如今的土耳其的伊斯坦布尔,是东罗马帝国的首都.由于当时拜占庭罗马帝国国土辽阔,为了防御目的,因此每个军队都分隔很远,将军与将军之间只能靠信差传消息.在战争的时候,拜占庭军队内所有将军必需达成 一致的共识,决定是否有赢的机会才去攻打敌人的阵营.但是,在军队内

【转】Raft 为什么是更易理解的分布式一致性算法

编者按:这是看过的Raft算法博客中比较通俗的一篇了,讲解问题的角度比较新奇,图文并茂,值得一看.原文链接:Raft 为什么是更易理解的分布式一致性算法 一致性问题可以算是分布式领域的一个圣殿级问题了,关于它的研究可以回溯到几十年前. 拜占庭将军问题 Leslie Lamport 在三十多年前发表的论文<拜占庭将军问题>(参考[1]). 拜占庭位于如今的土耳其的伊斯坦布尔,是东罗马帝国的首都.由于当时拜占庭罗马帝国国土辽阔,为了防御目的,因此每个军队都分隔很远,将军与将军之间只能靠信差传消息.

【转载】Raft 为什么是更易理解的分布式一致性算法

一致性问题可以算是分布式领域的一个圣殿级问题了,关于它的研究可以回溯到几十年前. 拜占庭将军问题 Leslie Lamport 在三十多年前发表的论文<拜占庭将军问题>(参考[1]). 拜占庭位于如今的土耳其的伊斯坦布尔,是东罗马帝国的首都.由于当时拜占庭罗马帝国国土辽阔,为了防御目的,因此每个军队都分隔很远,将军与将军之间只能靠信差传消息.在战争的时候,拜占庭军队内所有将军必需达成 一致的共识,决定是否有赢的机会才去攻打敌人的阵营.但是,在军队内有可能存有叛徒和敌军的间谍,左右将军们的决定又

Raft 为什么是更易理解的分布式一致性算法——(1)Leader在时,由Leader向Follower同步日志 (2)Leader挂掉了,选一个新Leader,Leader选举算法。

转自:http://www.cnblogs.com/mindwind/p/5231986.html Raft 协议的易理解性描述 虽然 Raft 的论文比 Paxos 简单版论文还容易读了,但论文依然发散的比较多,相对冗长.读完后掩卷沉思觉得还是整理一下才会更牢靠,变成真正属于自己的.这里我就借助前面黑白棋落子里第一种极简思维来描述和概念验证下 Raft 协议的工作方式. 在一个由 Raft 协议组织的集群中有三类角色: Leader(领袖) Follower(群众) Candidate(候选人

图解Raft:应该是最容易理解的分布式一致性算法

分布式一致性 想象一下,我们有一个单节点系统,且作为数据库服务器,然后存储了一个值(假设为X).然后,有一个客户端往服务器发送了一个值(假设为8).只要服务器接受到这个值即可,这个值在单节点上的一致性非常容易保证: 单机环境 但是,如果数据库服务器有多个节点呢?比如,如下图所示,有三个节点:a,b,c.这时候客户端对这个由3个节点组成的数据库集群进行操作时的值一致性如何保证,这就是分布式一致性问题.而Raft就是一种实现了分布式一致性的协议(还有其他一些一致性算法,例如:ZAB.PAXOS等):