分布式协议学习——一文彻底理解共识算法之Basic Paxos

前言

 在分布式系统中,为了提高可用性和可靠性一般采用多副本策略,虽然多副本确实能提高可靠性和可用性,但是也产生了一个新问题,那就是如何保证多个副本之间达成共识,也就是多个副本对任何一个提案(或者说 Op)是一致的。一个 Op 如何来达到共识就得采用共识算法。
  在过去很长一段时间,Paxos算法可以说是分布式共识的代名词,当前最常用的一批共识算法,比如,Fast Paxos算法,Cheap Paxos算法,比较有名的Raft算法也是基于它发展而来的。现实中,有很多基于Paxos的产品,比如Google的Chubby,Spanner,IBM的SVC等。

 Paxos算法包括两部分:

  • Basic Paxos算法。该算法描述的是多节点之间如何就某个值达成共识;
  • Multi-Paxos思想,它描述了执行多个Basic Paxos实例,就一系列值达成共识。赫赫有名的Raft算法就是一种基于Multi-Paxos思想的共识算法。
     那么,说到这这两者的关系,可以理解为Basic-Paxos是Multi-Paxos思想的核心。

什么是“角色”

 Paxos其实也类似,它有一个最重要的概念“角色”,在Paxos有三个“角色”,提议者(Proposer)、接受者(Acceptor)和学习者(Learner)。
 其中,提议者和接受者是这里最重要的两个角色,Paxos最核心的就是定义他们之间的通讯规则来保证某个变量在分布式系统中的一致性。下面分别介绍一下他们具体的作用:

  • 提议者(Proposer):提议者是提出议案的人,每个议案都有一个议案号,议案号是区别不同议案的唯一标识,而且议案号是有大小次序的。一般来说,集群中收到客户端请求的节点,才是提议者。

  • 接受者(Acceptor):对每个提议的值进行投票,并存储接收的值。通常来说,集群中的所有节点都扮演着接受者的角色,参与共识协商,并接收和存储数据。
      所以,经常会发现集群中的节点其实可以身兼多个“角色”。如下图那样,假设集群中有3个节点,当1个节点收到了请求,那么该节点作为提议者发起了二阶段提交,然后这个节点可以和另外两个节点一起作为接受者进行共识协商。
    在这里插入图片描述

  • 学习者(Learner):被告知投票的结果,接收达成的共识值,并存储保存。不参与投票的过程。通常,学习者是备份节点,就好比“Master-Slave”中的Slave,被动地接收数据,实现容灾备份。

 通过总结,这三个角色的功能如下:

角色 功能
提议者 接入和协调,对收到的客户端请求发起二阶段提议
接受者 投票协商和存储数据,对提议值进行投票并接受达成的共识的值,然后对共识值进行存储
学习者 存储数据,不参与共识协商,只对接收的共识的值进行存储保存

Basic Paxos算法

 在Basic Paxos是通过2PC算法达成共识的,它使用议案代表一个提议。不过在议案中除了议案号,还包括议案值。在这里,假设用[n,v]表示一个议案。其中k表示议案号,v表示议案值。
 整个共识协商分两个阶段完成。分别为准备阶段(Prepare)和接受阶段(Accept)

Proposal Numbers

一个Proposal是由Proposal Numbers和Proposal Value组成的。针对每一个Proposal都有一个唯一的ID,这个ID就是Proposal Numbers。它应具备以下两个特性:

  1. 它必须是全局唯一的,不管是同一个提议者提出的不同Proposal还是不同提议者提出的相同或者不同Poroposal
  2. 它是单调递增的,它的值越大则优先级越高。

一个propsal number 有两部分组成
在这里插入图片描述

  1. 当前是第几轮Round Number
  2. Server Id
      每个server都保存了一个maxRound,它表示server所看见的最大round number。如果产生了一个新的proposal number,则将maxRound递增,并且同server ld拼接。这个maxRound需要持久化到硬盘上,crash/重启之后不能复用原来的proposal number。

Prepare阶段

  1. 提议者发送提案
      提议者生成全局唯一且递增的提案号(proposal ID)后,向集群中所有结点发送提案,这里面不要携带提案内容,只携带提案号。
  2. 接受者响应提议者
      如果接受者以前已经有通过提案,将返回已通过的最大proposal ID的提案信息(value)以及已接受过的提案号(Accepted Proposal ID),没有则返回空值。
      如果准备请求的proposal ID小于等于接受者已经响应的准备请求的提案编号,那么接受者将不响应该准备请求;

Accept阶段

  1. 提议者发送接受请求
     提议者收集到多数派的响应后,从所有的响应提案中挑选那些包含value并且其proposal ID也是最大的提案。将其作为接受的提案,如果所有响应的提案的value均为空值,则可以自己决定提案value,然后携带上当前的proposal ID,向集群中的所有节点发送接受请求。

  2. 接受者响应接受请求
     如果接受请求中的提案的proposal ID小于等于接受者已经响应的接受请求的proposal ID,那么接受者将不通过这个提案。如果不违背该规则,接受者则持久化proposal ID和value。

例子分析

下面,我们通过一个历史故事来简单讲解下。
  战国中期,秦国商鞅变法后迅速崛起对外扩张,在函谷关外拉开大军,准备全面入侵山东国家,山东诸国惶惶不可终日,此时公孙衍犀首游说列国,并先后出任魏国韩国赵国等多个国相,提议欲抵抗秦国必须结成同盟,合纵方可破秦。最终有三个国家魏国,楚国,韩国集兵于函谷关外,各占一个山头准备攻打秦国。但是任意一个国家和秦国对抗必败,必须在三个国家中至少有两个国家达成一致,同时进攻。每个国家都有主帅(魏王,楚王,韩王)和参谋(魏相犀首,春申君黄歇),参谋给出的进攻时间必须得到魏楚韩中任意两个国家的同意,才可决议,但是派出信使有可能被秦国密探捕获,而导致信息丢失。所以达成一致的进攻时间是个难题。那么这里考虑采用Paxos协议来决议。

在这里插入图片描述

  1. 在准备阶段,参谋犀首派出信使送信给魏楚韩(议案号为1),魏王楚王收到信息,由于信使被捕,韩王未收到信息;
  2. 魏王楚王收到消息后,派信使告诉犀首已经收到信息,等待具体指示;
  3. 同时,参谋黄歇也派出信使给送信给魏楚韩(议案号2)。这次魏王未收到,楚王、韩王收到信息;
  4. 楚王韩王收到消息后,派信使告诉参谋黄歇已经收到信息,等待具体指示。

在这里插入图片描述
准备阶段完成后进入接受阶段
5. 参谋犀首收到消息后,派信使告诉魏楚王具体攻打日期是1号;
6. 魏王收到后觉得可以,告诉犀首1号进攻可以;
7. 楚王收到后,因他保存的最新指示议案号是2,而犀首的议案号1小于2,所以告诉犀首,1号方案不可行,正在等待2号方案;
8. 参谋黄歇派出信使告诉楚王韩王进攻时间为下个月2号;
9. 楚王收到后和自己的议案号对比,发现一致,则告诉参谋黄歇方案可行;
10. 韩王收到后,觉得参谋黄歇方案可行,则告诉参谋黄歇方案可行;
11. 参谋犀首得到的议案不是一半以上(魏王认可、楚王不认可,韩王未知),因此只有再次派出信使,通知最新的议案号为3。这里就进入了新一轮的准备阶段。如下图所示
在这里插入图片描述

  1. 新一轮的准备阶段,魏王收到信息后,告诉参谋犀首,我上次收到你的信息,是否更改,等待最新指示;
  2. 楚王收到信息后,告诉参谋犀首,我已经收到了议案编号3,我之前的议案值为2号,等待最新指示;
    在这里插入图片描述
  3. 进入新一轮的接受阶段,参谋犀首收到信息后,通过楚王的通知已经最新议案号为2,其对应的议案值为2号,所以这次派出信使发出信息“议案号为3,议案值为进攻日期2号”;
  4. 魏王收到后,按最新的指示执行,并回复参谋犀首;
  5. 楚王收到后,同自己保存的议案号对比,发现收到的议案号和保存的一致,则接受指示,并回复参谋犀首;
  6. 这样达成一致,半数以上同意2号。

Basic Paxos的容错

 除了实现了共识,Basic Paxos还实现了容错,它不像其它常用的分布式事务算法那样,要所有节点都同意后才提交操作,而是只要少于一半节点出错集群也是能正常工作的。

猜你喜欢

转载自blog.csdn.net/songguangfan/article/details/109392929