分布式一致性算法要解决哪些问题?

什么是分布式一致性?
简而言之,就是在分布式系统下要保证各个节点数据的一致性,也就是读和写两个操作都要保证一致性
在==单机情况==下解决一致性 涉及到并发操作或多线程环境下,可以采用事务或者加锁的方式来解决 但是在分布式系统下面,保证数据一致性的核心就是数据同步。但是在数据同步的过程中也会遇到一些问题:
- 服务的稳定性:各个节点(server)的状态不稳定,可能会宕机。
- 网络传输的稳定性:各个节点网络传输过程,网络发生抖动,同步请求会丢失。
- 网速问题:各个节点网络存在延迟,导致同步请求顺序发生改变
分布式一致性算法基于对CAP的遵循
CAP是 Consistency、Availability、Partition tolerance三个词语的缩写,分别表示一致性、可用性、分区容忍性。 是分布式系统处理数据一致性问题必须遵循的理论。 ①一致性 :对于客户端的每次读操作,要么读到的是最新的数据,要么读取失败。换句话说,一致性是站在分布式系统的角度,对访问本系统的客户端的一种承诺:要么我给您返回一个错误,要么我给你返回绝对一致的最新数据,不难看出,其强调的是数据正确。(要么数据正确,要么返回错误)
②可用性 :任何客户端的请求都能得到响应数据,不会出现响应错误。换句话说,可用性是站在分布式系统的角度,对访问本系统的客户的另一种承诺:我一定会给您返回数据,不会给你返回错误,但不保证数据最新,强调的是不出错。(不在乎数据是否正确,注重返回数据,但数据最终一致)
③ 分区容忍性 :由于分布式系统通过网络进行通信,网络是不可靠的。当任意数量的消息丢失或延迟到达时,系统仍会继续提供服务,不会挂掉。换句话说,分区容忍性是站在分布式系统的角度,对访问本系统的客户端的再一种承诺:我会一直运行,不管我的内部出现何种数据同步问题,强调的是不挂掉。(对于多个节点,其中一个挂掉不影响其它节点服务)
对于CAP的权衡:
在分布式系统中不可能同时满足CAP原理,只能满足其中的两个,但是为了保证分布式系统的健壮性,分区容忍性是必须要保证的,也就是说,在分布式系统中基于CAP原理,将保证分布式一致性分成两种,一种是强调强一致性==CA==,一种强调弱一致性(可用性)==AP==。 当选择了C(一致性)时,如果由于网络分区而无法保证特定信息是最新的,则系统将返回错误或超时。
当选择了A(可用性)时,系统将始终处理客户端的查询并尝试返回最新的可用的信息版本,即使由于网络分区而无法保证其是最新的。
对CAP举例
① 场景一: 有两个数据库,读写分离,主数据用来写数据,从数据用来读数据,某商品的价格是19999,双十一打折后9999,在双十一结束后,需要将主数据的9999改成19999,此时主数据已经修改,将去同步从数据库的数据,但是由于某种原因(服务器,网络)导致数据同步失败了,此时客户端去读从数据的价格还是9999元,这样就会导致出现损失,这种情况就要求强数据一致性。 ②场景二: 对于商品的评论信息,评论的数据展示不必要有及时性,这种情况如果是cp情况下,数据未同步,就会导致数据显示失败,评论就会加载失败。对于这种情况保证的是数据有,也就是可用性,用ap更合适。
分布式一致性算法
一般对于一致性算法基本都是讨论强一致性。 在了解分布式一致性算法在工程中实现之前,我们首先看看Mysql数据库主从复制的原理。这将有利于理解后面的Raft算法。
Binary Log(二进制日志):
- 作用: Binary log 主要用于记录数据库中的所有修改操作,包括 INSERT、UPDATE、DELETE 等,以二进制格式存储。
- 用途: 主要用于数据库恢复、点播恢复(point-in-time recovery)、以及主从复制。
- 存储位置: Binary log 存储在主服务器上。
Relay Log(中继日志):
- 作用: Relay log 用于存储从主服务器复制到从服务器的二进制日志事件。当从服务器从主服务器复制日志时,会先将主服务器的二进制日志内容保存到 Relay log 中。
- 用途: 主从复制中从服务器用 Relay log 来保存主服务器上的二进制日志信息,以便从服务器能够执行相同的修改操作。
- 存储位置: Relay log 存储在从服务器上。
- Master节点数据发生改变时,会将改动的sql写到Binary log日志文件中
- 对于Slave从节点而言,首先会开启一个I/O 线程用来读取主节点的Binary log日志文件
- 读取到数据以后,将数据写入到Relay log中
- 此时Slave,专门开启SQL线程来读取Relay log中的数据,并执行
这样就保证了MySQL中各个节点的数据一致性。
Paxos的两阶段决议机制
讲Raft前,先讲Paxos算法。
- Proposer: 提出提案 (Proposal)。Proposal信息包括提案编号 (Proposal ID) 和提议的值 (Value)。
- Acceptor:参与决策,回应Proposers的提案。收到Proposal后可以接受提案,若Proposal获得多数Acceptors的接受,则称该Proposal被批准。
- Learner:不参与决策,从Proposers/Acceptors学习最新达成一致的提案(Value)。
Paxos算法通过一个决议分为两个阶段(Learn阶段之前决议已经形成):
- 第一阶段:Prepare阶段。Proposer向Acceptors发出Prepare请求,Acceptors针对收到的Prepare请求进行Promise承诺。
- 第二阶段:Accept阶段。Proposer收到多数Acceptors承诺的Promise后,向Acceptors发出Propose请求,Acceptors针对收到的Propose请求进行Accept处理。
- 第三阶段:Learn阶段。Proposer在收到多数Acceptors的Accept之后,标志着本次Accept成功,决议形成,将形成的决议发送给所有Learners。
Paxos算法主要是基于多数决议保证数据的一致性的。如果多个节点的数据同步不一致,出现数据不一致的情况,按照少数服从多数的原则,保证数据一致性,这种思想也为后面的一致性算法比如Raft奠定了基础。
另外Paxos算法的效率比较低,每个决议需要两次请求(两个阶段)才能完成。但是在实际业务中,如果有大批量的消息需要决议,使用这种方式效率就很低了。
RAFT算法

算法流程:
- Client发送数据更改的请求,主节点Server收到请求后,放入Consensus Module (一致性模块)中
- Consensus Module 将请求给每个节点都发送一次,同时会将修改的请求放入Log日志文件
- Log日志文件将数据发送到State Machine中
- 最后返回给Clinet
选举机制
- 多个Server共同选举产生一个Leader节点。负责响应客户端的请求。
- Leader通过一致性决议,将客户端的指令发送到集群所有节点上。
- 每个节点将客户端的指令以Entry的形式保存在自己的Log日志中,此时Entry是uncommitted状态。
- 当有多数节点共同保存了entry之后,就可以执行Entry中的操作指令,提交到State Machine,此时Entry更新为committed状态。
领导者选举
Raft中节点的三种状态:从节点(node),候选节点(Candidate),主节点(Leader),所有的节点开始都是==从节点==,
此时如果没有节点收到主节点的信息,那么在从节点中就会有一个节点变成候选节点。 然后候选人向其他节点请求投票。
从节点在自身有票的情况下向候选节点投票,如果候选节点获得大多数节点(超过50%)的投票,则该候选节点成为主节点。
这个过程就是Raft的选举机制
选举机制
在 Raft 中,有两个超时设置来控制选举。选举超时是从节点等待成为候选节点的时间。选举超时随机时间为 150 毫秒到 300 毫秒之间。 选举时间结束从节点成为候选节点,开启新的任期
新的任期开始后,Term加一,并且候选节点向其余未成为候选节点的从节点发送投票请求,此时其余节点仍在等待选举超时。
从节点收到投票请求后,如果仍未投票,刷新选举超时时间,改变Term的值,并投票
一旦候选节点获得多数选票,它就会成为主节点。
成为主节点以后,向从节点追加消息,这些消息按==检测信号超时==指定的时间间隔发送。 丛节点收到消息后,会发送回信表示在工作状态。
这个选举任期将持续到从节点停止接收心跳并成为候选节点。依次按照上述过程重新选举新的候选节点和主节点。
两个候选节点
假如两个选举超时的时间相同,当选举超时时间结束以后,此时就会有两个候选节点产生,此时应该怎么解决呢? 如图,当A和D同时成为候选节点时,会同时向其余节点发送投票请求。 假如请求同时到达C和B
现在每个候选节点有 2 票,本届不能再获得选票。
此时,解决的办法就是,重新设置选举超时时间,直到新的候选节点产生。
Raft 日志复制
一旦我们选出了主节点,我们需要将对系统的所有更改复制到所有节点。这是通过每一次的心跳检测消息来完成的。 每个更改都放入节点日志中,但是该日志项当前++未提交++,因此不会更新节点的值。(红色表示未提交) 想要提交该数据,必须将该操纵同步到从节点的日志文件中
主节点向从节点发送同步信息,同时将结果返回给客户端,从节点收到主节点的同步信息,收到并返回信息告知主节点收到数据,此时主节点的日志文件的数据写入磁盘表示已经同步。
然后主节点告知从节点数据已提交,从节点提交数据,此时集群的节点中的数据已经是同步的状态。
网络分区
由于网络的延迟和网络的抖动,导致网络出现分区,此时由于选举机制会出现两个主节点,此时应该怎样保证数据一致性呢?
添加一个客户端来模拟 客户端2向NodeB发送set 3 的请求,此时主节点B,收到请求将请求写入日志文件中,此时向从节点A发送日志更新请求,A收到并回复, 此时B发现只有A回复,但是总共有五个节点,回复不到一般,不能保证更新。所以数据一直时未提交的状态。
此时于客户端1没有这种限制
当分区修复后,由于有两个主节点,怎样判断到底谁是最终的主节点呢? 此时就看主节点的Term的值,值越大表示有更高的选举期,而低的就会降级未从节点,并且数据会根据新的主节点进行更新