一致性算法

Posted qmillet

tags:

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

参考链接1 --分布式系列文章

参考连接2

一、何为分布式一致性

  C(一致性)A(高可用)P(分区容错性)理论:在满足CP的基础上尽可能提高可用性。

  何为一致性:一致性问题就是相互独立的节点之间如何达成一项决议的问题。分布式系统中,进行数据库事务提交(commit transaction)、Leader选举、序列号生成等都会遇到一致性问题(多数者理论,法院提案选举)。

  一致性又分为弱一致性(最终一致性,即最终结果对了就行)和强一致性。

   弱一致性有DNS、Gossip实现。

   强一致性可分为两类:

    1.主从同步,可参考mysql主从同步,Master接受写请求,复制日志到slave,Master等待,直到所有从库返回。但是如果一个节点失败,Master就会阻塞,导致整个集群不可用。

    2.多数派,每次写都保证写入大于N/2个节点,每次读保证从大于N/2个节点中读。相关算法:Paxos、Raft(multi-paxos)、ZAB(multi-paxos)

  什么样的系统才能满足一致性要求:

    1.全认同:所有节点都认同一个结果。

    2.值合法:结果必须由节点内部提出。

    3.可结束:决议过程在一定时间内可结束。

  分布式系统面临的问题:

    1.消息会出现延时、丢失,节点消息间不能做到同步有序。

    2.节点宕机或者出现逻辑问题。

    3.网络分化,即将节点分区。

二、2PC-tow phase commit  

1.角色划分:

  coordinator(协调者):提出提议的节点。

  participants/cohorts(参与者):参与决议的节点。

2.两阶段划分:

  在阶段一中,协调者发起一个提议,分别询问参与者是否接受。

 技术图片

  在阶段二中,协调者根据参与者的反馈,提交或者终止事务。如果参与者全部同意则提交事务,只要有一个参与者不同意就终止事务。

技术图片

3.存在问题

  协调者如果发起提议后宕机,那么参与者会进入阻塞状态,等待参与者回应完成此次决议。此时需要一个协调者备份角色解决此问题,协调者宕机一段时间后,协调者备份接替协调者的工作,通过问询参与者的状态决定阶段2是否提交事务。这也要求协调者/参与者记录历史状态,以备协调者备份对参与者查询。重新恢复状态。

三、3PC-three phase commit

  三阶段提交增加了一个准备提交(prepare to commit)阶段解决patticpant宕机问题。

  具体过程如下图:

技术图片

四、Paxos

4.1 Basic Paxos

4.1.1 角色划分

  Client:系统外部角色。请求发起者——民众。

  Proposer:接受Client请求,向集群提出提议(propose),并在冲突发生时,起到冲突调解的作用。像议员,替民众提出议案。

  Acceptor:提议投票和接受者,只有在形成法定人数(Quorum,一般即为majority-多数派)时,提议才会最终被接受。像国会。

  Learer:提议接受者,backup-备份,对集群一致性没什么影响。像记录员。

4.1.2 步骤

  4.1.2.1 Prepare

  proposer提出一个**提议,编号为N,**此N大于这个proposer之前提出的提案编号。请求acceptors的quorum接受。

  4.1.2.2 Promise

  如果N大于此acceptor之前接受的任何提案编号则接受,否则拒绝。

  4.1.2.3 Accept

  如果达到了多数派,proposer会发出 accept请求,此请求包含提案编号N,以及提案内容。

  4.1.2.4 Accepted

  如果此acceptor在此期间没有收到任何编号大于N的提案,则接受此提案内容,否则忽略。

Client   Proposer      Acceptor     Learner  
   |         |          |  |  |       |  |
   X-------->|          |  |  |       |  |  Request
   |         X--------->|->|->|       |  |  4.2.1 Prepare(1) 
   |         |<---------X--X--X       |  |  4.2.2 Promise(1,{Va,Vb,Vc})
   |         X--------->|->|->|       |  |  4.2.3 Accept!(1,V)
   |         |<---------X--X--X------>|->|  4.2.4 Accepted(1,V)
   |<---------------------------------X--X  Response
   |         |          |  |  |       |  |

 

  如果一个Acceptor宕机,只要满足多数派则可继续执行。

Client   Proposer      Acceptor     Learner
   |         |          |  |  |       |  |
   X-------->|          |  |  |       |  |  Request
   |         X--------->|->|->|       |  |  Prepare(1)
   |         |          |  |  !       |  |  !! FAIL !!
   |         |<---------X--X          |  |  Promise(1,{Va, Vb, null})
   |         X--------->|->|          |  |  Accept!(1,V)
   |         |<---------X--X--------->|->|  Accepted(1,V)
   |<---------------------------------X--X  Response
   |         |          |  |          |  |

 

  如果Learner宕机,可继续执行成功。

Client Proposer         Acceptor     Learner
   |         |          |  |  |       |  |
   X-------->|          |  |  |       |  |  Request
   |         X--------->|->|->|       |  |  Prepare(1)
   |         |<---------X--X--X       |  |  Promise(1,{Va,Vb,Vc})
   |         X--------->|->|->|       |  |  Accept!(1,V)
   |         |<---------X--X--X------>|->|  Accepted(1,V)
   |         |          |  |  |       |  !  !! FAIL !!
   |<---------------------------------X     Response
   |         |          |  |  |       |

 

  如果一个Proposser在提出提议后宕机,会选出新的Leader接收Accepor反馈。

Client  Proposer        Acceptor     Learner
   |      |             |  |  |       |  |
   X----->|             |  |  |       |  |  Request
   |      X------------>|->|->|       |  |  Prepare(1)
   |      |<------------X--X--X       |  |  Promise(1,{Va, Vb, Vc})
   |      |             |  |  |       |  |
   |      |             |  |  |       |  |  !! Leader fails during broadcast !!
   |      X------------>|  |  |       |  |  Accept!(1,V)
   |      !             |  |  |       |  |
   |         |          |  |  |       |  |  !! NEW LEADER !!
   |         X--------->|->|->|       |  |  Prepare(2)
   |         |<---------X--X--X       |  |  Promise(2,{V, null, null})
   |         X--------->|->|->|       |  |  Accept!(2,V)
   |         |<---------X--X--X------>|->|  Accepted(2,V)
   |<---------------------------------X--X  Response
   |         |          |  |  |       |  |

 

4.1.3 存在问题

  4.1.3.1 活锁和dueing(决斗)

    多个Prosesser提议者都认为自己是领导者,例如Prosesser1发出提议1之后,Prosesser2也发出提议2,此时Prosesser1的提议就会被拒绝,会再次发出提议3,然后Prosesser2的提议2就会被拒绝发出提议4、、、、

  

Client   Leader         Acceptor     Learner
|      |             |  |  |       |  |
X----->|             |  |  |       |  |  Request
|      X------------>|->|->|       |  |  Prepare(1)
|      |<------------X--X--X       |  |  Promise(1,{null,null,null})
|      !             |  |  |       |  |  !! LEADER FAILS
|         |          |  |  |       |  |  !! NEW LEADER (knows last number was 1)
|         X--------->|->|->|       |  |  Prepare(2)
|         |<---------X--X--X       |  |  Promise(2,{null,null,null})
|      |  |          |  |  |       |  |  !! OLD LEADER recovers
|      |  |          |  |  |       |  |  !! OLD LEADER tries 2, denied
|      X------------>|->|->|       |  |  Prepare(2)
|      |<------------X--X--X       |  |  Nack(2)
|      |  |          |  |  |       |  |  !! OLD LEADER tries 3
|      X------------>|->|->|       |  |  Prepare(3)
|      |<------------X--X--X       |  |  Promise(3,{null,null,null})
|      |  |          |  |  |       |  |  !! NEW LEADER proposes, denied
|      |  X--------->|->|->|       |  |  Accept!(2,Va)
|      |  |<---------X--X--X       |  |  Nack(3)
|      |  |          |  |  |       |  |  !! NEW LEADER tries 4
|      |  X--------->|->|->|       |  |  Prepare(4)
|      |  |<---------X--X--X       |  |  Promise(4,{null,null,null})
|      |  |          |  |  |       |  |  !! OLD LEADER proposes, denied
|      X------------>|->|->|       |  |  Accept!(3,Vb)
|      |<------------X--X--X       |  |  Nack(4)
|      |  |          |  |  |       |  |  ... and so on ...

 

  解决办法:如果发生冲突,则Proposer等待一个Random的Timeout(一般几秒)再提交自己的提议。

  4.1.3.2 难实现,效率低

  Basic Paxos的难度是较为出名的,且不易理解。提交提议、提交提案(日志)内容进行了两轮RTT操作,效率较低。 

 

4.2 Multi Paxos

4.2.1 角色

  Leader:唯一的Prosesser,所有请求都需经过此Leader。

  Client:客户端

  Server:服务短

4.2.2 实现过程

Client   Proposer      Acceptor     Learner
|         |          |  |  |       |  | --- First Request ---
X-------->|          |  |  |       |  |  Request
|         X--------->|->|->|       |  |  Prepare(N)   N是提议版本
|         |<---------X--X--X       |  |  Promise(N,I,{Va,Vb,Vc}) I是Leader版本
|         X--------->|->|->|       |  |  Accept!(N,I,V)
|         |<---------X--X--X------>|->|  Accepted(N,I,V)
|<---------------------------------X--X  Response
|         |          |  |  |       |  |

------------------------------------------------------------------
Client   Proposer       Acceptor     Learner
|         |          |  |  |       |  |  --- Following Requests ---
X-------->|          |  |  |       |  |  Request
|         X--------->|->|->|       |  |  Accept!(N,I+1,W)
|         |<---------X--X--X------>|->|  Accepted(N,I+1,W)
|<---------------------------------X--X  Response
|         |          |  |  |       |  |
------------------------------------------------------------------
Client      Servers
X-------->|  |  |  Request
|         X->|->|  Accept!(N,I+1,W)
|         X<>X<>X  Accepted(N,I+1)
|<--------X  |  |  Response
|         |  |  |

 

 

五、Raft

5.1 角色

  Leader:主节点,整个集群只有一个Leader,所有的写请求都通过Leader发送给Follower;在每一个Leader的任期期间,都有唯一表示该任期的Term。

  Follower:从节点(服从角色);

  Candidate:在Leader消息发送失败或宕机,整集群没有Leader时,此时Follower接收Leader的心跳包失败,则Follwer开始竞选Leader时,它们的身份是Candidate。Candidate只是个中间状态,不会长期存在。

5.2 基本操作

5.2.1 Leader Election

  集群启动或Leader的心跳包消息无法发送给Follower时,触发 Leader Election——选主 操作。

5.2.2 Log Replication

  1、所有的写请求都要经过Leader;
  2、Leader将写请求携带在心跳包中发送给Follower;
  3、当Leader收到多数派回复的消息后,则先自己提交写操作,同时发送Commit请求给Follower;

5.2.3 Safety保证 

 1、Leader宕机感知:
  a、Raft通过TimeOut来保证Follower能正确感知Leader宕机或消息丢失的事件,并触发Follower竞选Leader;
  b、Leader需要给Follower发送心跳包(heartbeats),数据也是携带在心跳包中发送给Follower的;

 2、选主平票情况
  Leader Election时平票情况下,则两个Candidates会产生一个随机的Timewait,继续发送下一个竞选消息。

 3、脑裂(大小集群)情况:
  小集群由于没有得到多数派的回复,写操作失败;
  大集群会发生重新选主的过程,且新Leader拥有自己新的Term(任期),写操作成功;
  当小集群回到大集群时,由于小集群的Term小于新集群的Term,则同步新集群的信息。

 

5.3 一致性 = 客户端 + 一致性算法

 Client Request操作的三个可能结果:成功、失败、unknown(Timeout)

 5.3.1理解unknown(Timeout)
  场景:Client写请求,Leader向Follower同步日志,此时集群中有3个节点失败,2个节点存活,结果是? 假设节点为:S1、S2、S3、S4、S5(Leader)

  假设S5和S4存活,Client发起 第N次写请求 为 操作I 时,由于Leader没有得到多数派的回复,操作I只被发送到了S4中,此时Leader即会返回Client unknown,因为Leader不知道后面会不会成功将该条日志写入多数派中。
  结果1:假设Leader在返回客户端后,宕机的Follower:S1、S2、S3恢复正常,Leader再次发送 第N次写请求——操作I,且得到了多数派的回复,则提交日志,写操作最终结果为成功;
  结果2:假设Leader在返回客户端后,此时S5和S4宕机,且S1、S2、S3恢复正常,此时S1、S2、S3触发选主操作,且集群恢复可用,如果此时Client发起 第N+1次请求 为 操作I+1 ,且Client操作成功后 S5、S4恢复正常,则保存在S5、S4中的 操作I 会被删除,S5、S4同步最新的 操作I+1 到本地。则 第N次写请求—操作I 失败;

  总结:一致性需要客户端和共识算法(Consensus)来共同保证。

 

六、ZAB - Zookeeper atomic broadcast protocol
  ZAB是Zookeeper内部用到的一致性协议。基本与Raft相同。  在一些名词的叫法上有些区别:如ZAB将某一个leader的周期称为epoch,而Raft则称为Term。  实现上也有些许不同:Raft保证日志连续性,心跳方向为Leader至Follower。ZAB则相反。

以上是关于一致性算法的主要内容,如果未能解决你的问题,请参考以下文章

有人可以解释啥是 SVN 平分算法吗?理论上和通过代码片段[重复]

片段(Java) | 机试题+算法思路+考点+代码解析 2023

架构实践使用 golang 实现一致性Hash算法代码

跨活动的片段之间的共享元素转换不一致

对一致性Hash算法,Java代码实现的深入研究

对一致性Hash算法,Java代码实现的深入研究