分布式事务与一致性算法Paxos & raft & zab

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式事务与一致性算法Paxos & raft & zab相关的知识,希望对你有一定的参考价值。

1.CAP原理

  • 要想数据高可用,就得写多份数据

  • 写多分数据就会导致数据一致性问题

  • 数据一致性问题会引起性能问题

2.一致性模型

  1. 弱一致性

  2. 最终一致性(一段时间达到一致性)

  3. 强一致

  • 1、2 异步冗余;3是同步冗余

3.  扩展服务的方案

  • 数据分区: uid % 16

  • 数据镜像:让多有的服务器都有相同的数据,提供相当的服务(冗余存储,一般3份为好)

4.两种方案的事务问题

  1. A向B汇钱,两个用户不在一个服务器上

  2. 镜像:在不同的服务器上对同一数据的写操作如何保证一致性。 

5. 解决一致性事务问题的技术

1. Master -Slave

  • 读写请求由Master负责

  • 写请求写到Master后,由Master同步到Slave上

    • 由Master push or Slave pull

    • 通常是由Slave 周期性来pull,所以是最终一致性

问题: 若在 pull 周期内(不是期间?),master挂掉,那么会导致这个时间片内的数据丢失

  • 若不想让数据丢掉,Slave 只能成为 ReadOnly方式等Master恢复

  • 若容忍数据丢失,可以让 Slave代替Master工作

如何保证强一致性?

  • Master 写操作,写完成功后,再写 Slave,两者成功后返回成功。若 Slave失败,两种方法

    1. 标记 Slave 不可用报错,并继续服务(等恢复后,再同步Master的数据,多个Slave少了一个而已)

    2. 回滚自己并返回失败

2. Master-Master

  • 数据同步一般是通过 Master 间的异步完成,所以是最终一致

  • 好处: 一台Master挂掉,另外一台照样可以提供读写服务。当数据没有被赋值到别的Master上时,数据会丢失。

  • 对同一数据的处理问题:Dynamo的Vector Clock的设计(记录数据的版本号和修改者),当数据发生冲突时,要开发者自己来处理

3.两阶段提交  Two  Phase Commit   _ 2PC

  1. 第一阶段:针对准备工作

    • 协调者问所有节点是否可以执行提交

    • 参与者开始事务,执行准备工作:锁定资源(获取锁操作)

    • 参与者响应协调者,如果事务的准备工作成功,则回应"可以提交",否则,拒绝提交

  2. 第二阶段:

    • 若都响应可以提交,则协调者项多有参与者发送正式提交的命令(更新值),参与者完成正式提交,释放资源,回应完成。协调者收到所有节点的完成响应后结束这个全局事务.。若参与者回应拒绝提交,则协调者向所有的参与者发送回滚操作,并释放资源,当收到全部节点的回滚回应后,取消全局事务

  3. 存在的问题:若一个没提交,就会进行回滚

    • 第一阶段:若消息的传递未接收到,则需要协调者作超时处理,要么当做失败,要么重载

    • 第二阶段:若参与者的回应超时,要么重试,要么把那个参与者即为问题节点,提出整个集群

    • 在第二阶段中,参与者未收到协调者的指示(也许协调者挂掉),则所有参与者会进入“不知所措” 的状态(但是已经锁定了资源),所以引入了三段提交

4. 三段提交:把二段提交的第一阶段 break 成了两段

  1. 询问

  2. 锁定资源(获取锁)

  3. 提交

  • 核心理念:在询问的时候并不锁定资源,除非所有人都同意了,才开始锁定

  • 好处:当发生了失败或超时时,三段提交可以继续把状态变为Commit 状态,而二段提交则不知所措?

5. Raxos 算法(少数服从多数)

  • 解决的问题:在一个可能发生异常的分布式系统中如何就某个值达成一致,让整个集群的节点对某个值的变更达成一致

  • 任何一个节点都可以提出要修改某个数据的提案,是否通过这个提案取决于这个集群中是否有超过半数的节点同意(所以节点数总是单数)—— 版本标记。虽然一致性,但是只能对一个操作进行操作啊??

  • 当一个Server接收到比当前版本号小的提案时,则拒绝。当收到比当前大的版本号的提案时,则锁定资源,进行修改,返回OK.   也就是说收到超过一半的最大版本的提案才算成功。

核心思想:

  1. 在抢占式访问权的基础上引入多个acceptor,也就是说当一个版本号更大的提案可以剥夺版本号已经获取的锁。

  2. 后者认同前者的原则:

    • 在肯定旧epoch 无法生成确定性取值时,新的 epoch 会提交自己的valu

    • 一旦 旧epoch形成确定性取值,新的 epoch肯定可以获取到此取值,并且会认同此取值,不会被破坏。

步骤

  1. P1 请求Acceptor的 #1,Acceptor 这时并没有其他线程获取到锁,所以把锁交给 P1,并返回这时 #1 的值为null分布式事务与一致性算法Paxos & raft & zab

  2. 然后 P1 向 第一个 Acceptor 提交 #1 的值,Acceptor 接受并返回 OK

    分布式事务与一致性算法Paxos & raft & zab

  3. 这个时候,P2向Acceptor请求#1上的锁,因为版本号更大,所以直接抢占了 P1 的锁。这时 Acceptor 返回了 OK并且返回了 #1 的值

    分布式事务与一致性算法Paxos & raft & zab

  4. 这时 P1 P向 后面两个 Acceptor 提交 #1 的值,但是由于中间的那个Acceptor 版本号已经更改为 2 了,所以拒绝P1。第三个 Acceptor 接受了,并且返回了 OK

    分布式事务与一致性算法Paxos & raft & zab

  5. 由于后者认同前者的原则,这时 P1 已经形成确定性取值了 V1 了,这时新的 P2 会认同此取值,而不是提交自己的取值。所以,P2会选择最新的那个取值 也就是V1 进行提交。这时Acceptor 返回 OK

    分布式事务与一致性算法Paxos & raft & zab

6.ZAB 协议 ( Zookeeper Atomic  Broadcast) 原子广播协议:保证了发给各副本的消息顺序相同

定义:原子广播协议 ZAB 是一致性协议,Zookeeper 把其作为数据一致性的算法。ZAB 是在 Paxos 算法基础上进行扩展而来的。Zookeeper 使用单一主进程 Leader用于处理客户端所有事务请求,采用 ZAB 协议将服务器状态以事务形式广播到所有 Follower 上,由于事务间可能存在着依赖关系,ZAB协议保证 Leader 广播的变更序列被顺序的处理,一个状态被处理那么它所依赖的状态也已经提前被处理

核心思想:保证任意时刻只有一个节点是Leader,所有更新事务由Leader发起去更新所有副本 Follower,更新时用的是 两段提交协议,只要多数节点 prepare 成功,就通知他们commit。各个follower 要按当初 leader 让他们 prepare 的顺序来 apply 事务

协议状态

  1. Looking:系统刚启动时 或者 Leader 崩溃后正处于选举状态

  2. Following:Follower 节点所处的状态,Follower与 Leader处于数据同步状态

  3. Leading:Leader 所处状态,当前集群中有一个 Leader 为主进程

  • ZooKeeper启动时所有节点初始状态为Looking,这时集群会尝试选举出一个Leader节点,选举出的Leader节点切换为Leading状态;当节点发现集群中已经选举出Leader则该节点会切换到Following状态,然后和Leader节点保持同步;当Follower节点与Leader失去联系时Follower节点则会切换到Looking状态,开始新一轮选举;在ZooKeeper的整个生命周期中每个节点都会在Looking、Following、Leading状态间不断转换。

  • 分布式事务与一致性算法Paxos & raft & zab

  • 选举出Leader节点后 ZAB 进入原子广播阶段,这时Leader为和自己同步每个节点 Follower 创建一个操作序列,一个时期一个 Follower 只能和一个Leader保持同步

阶段

  1. Election: 在 Looking状态中选举出 Leader节点,Leader的LastZXID总是最新的(只有lastZXID的节点才有资格成为Leade,这种情况下选举出来的Leader总有最新的事务日志)。在选举的过程中会对每个Follower节点的ZXID进行对比只有highestZXID的Follower才可能当选Leader

    • 每个Follower都向其他节点发送选自身为Leader的Vote投票请求,等待回复;

    • Follower接受到的Vote如果比自身的大(ZXID更新)时则投票,并更新自身的Vote,否则拒绝投票;

    • 每个Follower中维护着一个投票记录表,当某个节点收到过半的投票时,结束投票并把该Follower选为Leader,投票结束;

  2. Discovery:Follower 节点向准 Leader推送 FollwerInfo,该信息包含了上一周期的epoch,接受准 Leader 的 NEWLEADER 指令

  3. Sync:将 Follower 与 Leader的数据进行同步,由Leader发起同步指令,最终保持数据的一致性

  4. Broadcast:Leader广播 Proposal 与 Commit,Follower 接受 Proposal 与 commit。因为一个时刻只有一个Leader节点,若是更新请求,只能由Leader节点执行(若连到的是 Follower 节点,则需转发到Leader节点执行;读请求可以从Follower 上读取,若是要最新的数据,则还是需要在 Leader上读取)

    • 消息广播使用了TCP协议进行通讯所有保证了接受和发送事务的顺序性。广播消息时Leader节点为每个事务Proposal分配一个全局递增的ZXID(事务ID),每个事务Proposal都按照ZXID顺序来处理(Paxos 保证不了)

    • Leader节点为每一个Follower节点分配一个队列按事务ZXID顺序放入到队列中,且根据队列的规则FIFO来进行事务的发送。

  5. Recovery :根据Leader的事务日志对Follower 节点数据进行同步更新

    • 同步策略:

    • Follower将所有事务都同步完成后Leader会把该节点添加到可用Follower列表中;

    • Follower接收Leader的NEWLEADER指令,如果该指令中epoch比当前Follower的epoch小那么Follower转到Election阶段

    1. SNAP :如果Follower数据太老,Leader将发送快照SNAP指令给Follower同步数据;

    2. DIFF :Leader发送从Follolwer.lastZXID到Leader.lastZXID议案的DIFF指令给Follower同步数据;

    3. TRUNC :当Follower.lastZXID比Leader.lastZXID大时,Leader发送从Leader.lastZXID到Follower.lastZXID的TRUNC指令让Follower丢弃该段数据;(当老Leader在Commit前挂掉,但是已提交到本地)

7. Raft 算法

  • Raft 算法也是一种少数服从多数的算法,在任何时候一个服务器可以扮演以下角色之一:

    1. Leader:负责 Client 交互 和 log 复制,同一时刻系统中最多存在一个

    2. Follower:被动响应请求 RPC,从不主动发起请求 RPC

    3. Candidate : 由Follower 向Leader转换的中间状态

  • 在选举Leader的过程中,是有时间限制的,raft 将时间分为一个个 Term,可以认为是“逻辑时间”:

    1. 每个 Term中至多存在1个 Leader

    2. 某些 Term由于不止一个得到的票数一样,就会选举失败,不存在Leader。则会出现 Split Vote  ,再由候选者发出邀票

    3. 每个 Server 本地维护 currentTerm

  • 选举过程:

    • 获得超过半数的Server的投票,转换为 Leader,广播 HeatBeat

    • 接收到 合法 Leader 的 AppendEnties RPC,转换为Follower

    • 选举超时,没有 Server选举成功,自增 currentTerm ,重新选举

    1. 自增 CurrentTerm,由Follower 转换为 Candidate,设置 votedFor 为自身,并行发起 RequestVote RPC,不断重试,直至满足下列条件之一为止:

    2. 当Candidate 在等待投票结果的过程中,可能会接收到来自其他Leader的 AppendEntries RPC ,如果该 Leader 的 Term 不小于本地的 Current Term,则认可该Leader身份的合法性,主动降级为Follower,反之,则维持 candida 身份继续等待投票结果

    3. Candidate 既没有选举成功,也没有收到其他 Leader 的 RPC (多个节点同时发起选举,最终每个 Candidate都将超时),为了减少冲突,采取随机退让策略,每个 Candidate 重启选举定时器

  • 日志更新问题:

    如果在日志复制过程中,发生了网络分区或者网络通信故障,使得Leader不能访问大多数Follwers了,那么Leader只能正常更新它能访问的那些Follower服务器,而大多数的服务器Follower因为没有了Leader,他们重新选举一个候选者作为Leader,然后这个Leader作为代表于外界打交道,如果外界要求其添加新的日志,这个新的Leader就按上述步骤通知大多数Followers,如果这时网络故障修复了,那么原先的Leader就变成Follower,在失联阶段这个老Leader的任何更新都不能算commit,都回滚,接受新的Leader的新的更新。

  • 流程:

    • 解决办法:Client 赋予每个 Command唯一标识,Leader在接收 command 之前首先检查本地log

    1. Client 发送command 命令给 Leader

    2. Leader追加日志项,等待 commit 更新本地状态机,最终响应 Client

    3. 若 Client超时,则不断重试,直到收到响应为止(重发 command,可能被执行多次,在被执行但是由于网络通信问题未收到响应)

9. paxos 算法与 raft 算法的差异

  1. raft强调是唯一leader的协议,此leader至高无上

  2. raft:新选举出来的leader拥有全部提交的日志,而 paxos 需要额外的流程从其他节点获取已经被提交的日志,它允许日志有空洞

  • 相同点:得到大多数的赞成,这个 entries 就会定下来,最终所有节点都会赞成

NWR模型

  • N: N个备份

  • W:要写入至少 w 份才认为成功

  • R : 至少读取 R 个备份

  • W+ R > N    ——>    R > N - W(未更新成功的) ,代表每次读取,都至少读取到一个最新的版本(更新成功的),从而不会读到一份旧数据

  • 问题:并非强一致性,会出现一些节点上的数据并不是最新版本,但却进行了最新的操作

  • 版本冲突问题:矢量钟 Vector Clock : 谁更新的我,我的版本号是什么(对于同一个操作者的同一操作,版本号递增)

参考资料:



.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

以上是关于分布式事务与一致性算法Paxos & raft & zab的主要内容,如果未能解决你的问题,请参考以下文章

分布式技术专题「分布式技术架构」一文带你厘清分布式事务协议及分布式一致性协议的算法原理和核心流程机制(Paxos篇)

搞懂分布式技术2:分布式一致性协议与Paxos,Raft算法

四,分布式事务一致性协议paxos通俗理解

Zookeeper与Paxos

从Paxos到Zookeeper分布式一致性原理与实践 -笔记

从Paxos到Zookeeper分布式一致性原理与实践 -笔记