分布式 | Paxos 共识算法

1、简介

  • Paxos共识算法 是非拜占庭容错算法的一种, 用来解决分布式下存在 故障行为 但不存在 恶意行为的共识问题
  • 是一种强一致性模型, 需要半数以上的读取或者写入成功才算操作成功.
  • 当前最常用的共识算法如Fast Paxos算法、Cheap Paxos算法、Raft算法、ZAB协议等等都是根据Paxos算法改进而来
  • Paxos算法包含2种类型
    • Basic Paxos算法: 描述的是多节点之间如何就某个值(提案的Value)达成共识
    • Multi-Paxos思想: 描述的是执行多个Basic Paxos实例,就一系列值达成共识

2、状态机复制

所谓的保证一致性到底是保证的是什么?
1、一般来说, 分布式的一致性的解决方案都是通过状态机复制来解决, 比如每个操作就是一条日志, 而每个节点都有自己的操作日志, 而所谓的保证一致性就是保证节点间日志的一致性, 通过与其他节点进行日志的同步.

2、当日志保证一致了, 相同的日志序列输入到相同的状态机然后输出后最后得到的结果是一样的.

在这里插入图片描述
关于状态机

  • 状态机实现目标是: 相同的输入序列得到的输出是一致的. 比如每台服务器都运行着相同的相同的状态机S, 然后刚开始每个状态机都处于初始状态S0, 然后这些状态机计算相同的输入序列{x0, x1, x2, x3, ...xn}, 那么这些状态机一定会经过相同的状态转换过程为: S0->S1->S2->S3...Sn, 最终都达到了相同的状态Sn, 并且输出相同的结果序列为: {out1(S1), out2(S2), out3(S3), .... outn(Sn)}.

比如有以下输入序列(从上到下), 只要每台服务器有相同的状态机, 并且处理输入序列之前状态机都处于相同的状态, 那么经过状态机后, 它们最终达到的状态都是一致的, 比如经过指令重放来实现状态机, 那么经过输入序列后最终a都等于4, b都等于3达到相同的状态.
在这里插入图片描述

3、Base Paxos 算法

3.1 决策模型

  • 客户端(Client):
    • 负责发起读写请求
  • 提议者(Proposer):
    • 负责对客户端的请求在内部发起一个提案 用于投票表决.
    • 代表的是接入和协调功能,收到客户端请求后,发起二阶段提交,进行共识协商;
  • 接收者(Acceptor):
    • 负责对每个提案进行投票表决, 并在本地存储自己投票过的提案
    • 代表投票协商和存储数据,对提议的值进行投票,并接受达成共识的值,存储保存;
  • 学习者(Learner):
    • 当投票通过后存储该提案的投票结果(也就是达成共识的结果)
    • 代表存储数据,不参与共识协商,只接受达成共识的值,存储保存。

在这里插入图片描述
大致的共识协商流程:

  • 在一阶段, client想要发起一个提案给提议者Proposer(比如一个写请求set a=3之类), 然后提议者再发动提案请求比如[1,null]传递给第一个接受者Acceptor, 然后基于状态机复制模型进行将提案请求复制到其他接受者Accpetor, 当超过半数以上的接受者Acceptor响应表示允许发出一个提案, 说明一阶段准备请求成功. 然后提议者开始二阶段, 提议者把提案id及其内容(如[1,3])发给接受者Accpetor, 然后同样的吐过超过半数以上的响应说明提案通过, 然后该通过的提案就会被学习者进行持久化存储记录.

3.2 Base Paxos 如何达成共识

  • 是通过提议者(Proposer) 发起二阶段提交来完成一次共识协商,

一阶段叫准备阶段:

  • 1)Prepare准备请求阶段:
    • 主要是提议者去发出一个提案编号为N的提案准备请求, 并且这个提案N大于这个提议者之前提出的提案编号(要保证提案id全局唯一自增)
  • 2)Promise准备响应阶段
    • 主要是接受者去决定是否响应提议者的提案, 如果当前的提案小于该接受者之前接受的任何提案编号则拒绝响应, 否则接受并响应

二阶段叫接受阶段

  • 1)Accept接受请求阶段:
    • 提议者接收到接受者的半数以上的响应后, 就会进入二阶段, 开始发送提案接受请求 . 此次提案包含提案的编号和具体内容
  • 2)Accepted接受响应阶段
    • 主要是接受者去决定是否最终接受提议者的提案, 如果当前的提案小于该接受者之前接受的任何提案编号则拒绝接受, 否则接受.

[协商规则]

  • 接收者不接受小于它当前已保证的提案的ID的提案
  • 提案ID需要保证全局唯一自增, 目的是为了保证提案的有序性

3.2.1 案例1: 单个提案达成共识

一阶段

  • Prepare准备请求阶段, 提议者P发出了一个提案id为1的提案给3个接受者A、B、C(此时不需要携带提案内容所以为null),
  • 如图1 三个接受者分别在2点、3点、4点等3个时间点先后接收到了提议者P的一阶段提案请求.
  • 如图2. 由于接受者A、B、C在本地没有保证或者没有已通过的任何提案(如都为null), 所以响应提议者说他们可以接受此id的提案, 然后存储到本地, 并保证不再接受比此提案id更小的提案. 并发送Promise响应请求(如果本地之前没有保证或者通过任何提案, 则响应内容为空也就是尚无提案, 否则响应内容为已通过的提案[id, value]). 当提议者P接收到半数以上Promise准备响应阶段的成功后, 如图2为3个大于半数, 则可以进入下一个阶段[接受阶段].

在这里插入图片描述

在这里插入图片描述

二阶段

  • 由于之前收到大多数的接收者的Promise准备响应后进入到了Accept接受请求阶段. 开始发起接受请求, 并且这次会携带提案内容, 但是提案的内容会从一阶段的Promise准备响应阶段的响应内容中提案编号最大的提案的值作为此次提案的内容. 而如图2, 由于接收者A、B、C之前本地都没有通过任何提案, 所以返回 “[尚无提案]” 的响应, 其实也就是准备响应中都为空. 所以就把自己的提议值3作为此次的提案内容.
  • 在 Accepted接受响应阶段, 由于提案的提案编号1不小于接收者A、B、C在本地已保证或者已通过的提案id, 所以都通过了提案[1,3], 于是接受了值7的写入, 三个节点就此达成共识

在这里插入图片描述

在这里插入图片描述

3.2.2 案例2: 并发提案达成共识

一阶段的Prepare准备请求阶段, 提议者P先发起了提案[1:null], 在1、2时间点接受者A、B接收到后, 因为本地没有已通过的提案,所以接受者A、B会保证该提案, 并保证不再响应比该提案id[1]更小的提案。随后在3点的时间线提议者Q也发起了
一个提案[6,null],接收者A、B、C分别在3点、4点、5点接收到了请求, 因为当前提案6比接收者A、B本地保证的提案1比大,
所以接收者A、B会更改保证的提案为6, 而接受者C本地没有已通过的提案也没有保证任何提案所以会接收该提案, 并更新保证提案为6。
随后在时间点6, 接收者C接收到了提议者P的提案请求[1,null], 而由于接收者C已保证了提案6不再响应比提案6更小的提案,所以拒绝提案1。

在一阶段的Promise准备响应阶段, 由于接收者A、B、C本地都没有通过任何提案,所以响应给提议者P和Q的内容都是尚无提案
(但是如果已经通过了提案,会将通过的提案[id,value]作为响应内容)。

在二阶段的Accept接收请求阶段, 提议者P收到了接收者A和B等2个响应。所以超过了半数以上接收者的响应,所以可以开始进行二阶段的提交请求阶段
并根据响应中提案编号最大的提案的值作为此次二阶段接受请求中提案的值。而由于接收者A和B的响应都为空(即尚无提案),所以把自己的提议值3作为提案的值,发送提案为[1, 3]的接受请求给接收者A、B、C。
而提议者Q收到了3个响应请求,也会开始进入二阶段, 同样也是根据响应中提案编号最大的提案的值作为此次二阶段接受请求中提案的值。
由于接收者A、B、C的响应都为空,所以把自己的提议值7作为提案的值, 发送提案为[6, 7]的接受请求给接收者A、B、C。

在二阶段的Accepted接受响应阶段, 接收者A、B、C、先收到了提议者P的接受请求的提案[1,3], 但是由于提案的id为1小于它们保证的提案6,
所以提案[1, 3]将被接收者A、B、C拒绝。随后接收者A、B、C收到了接收请求[6,7], 提案编号6不小于已保证的提案6,所以通过该提案[6,7],
也就是接受了值7,三个接收者就值7达成了共识。

在这里插入图片描述

3.2.3 案例3: 并发提案下产生的活锁问题

  • 其实每个节点既可以是提议者也可以是接收者, 因为每个节点都可以负责读写请求. 接收到写请求的那个节点会作为一个提议者去发起提案, 然后其他节点作为接收者进行投票表决. 所以可能同时存在多个提议者发起提案造成活锁问题
  • 所谓活锁问题就是所有提案都被拒绝并且一直持续下去, 我们知道接受者在遇到比当前已保证的提案小的情况下会拒绝当前提案, 产生活锁的原因就是并发提案下, 当前提案只完成了一阶段请求, 在准备进入二阶段请求之前, 该提案被其他提议者发出的更高级别的提案覆盖了, 导致后面要发出二阶段请求的时候被拒绝. 如果这么一直循环下去就会造成活锁.
  • 比如接受者A经历了 提案[1]的一阶段 ⇒ 提案[2]的一阶段 ⇒ 提案[1]的二阶段 => 提案[3] 的一阶段 => 提案2的二阶段 ⇒ 提案[4]的一阶段 ⇒ 提案[3]的二阶段..... 如果接收者A经过了这些状态转换过程, 那么提案1、2、3都会被接收者A拒绝. 同理那样交叉循环下去接收者A永远无法接受提案.

在这里插入图片描述

4 Multi Paxos 思想

  • Basic Paxos每次只能就单个值达成共识, 在并发提案下容易出现活锁问题
  • 我们实际上并不是提议者单独给每个接受者发送提案请求, 而是发送一个接收者后, 由它去进行复制同步到其他的接受者. 所以BasePaxos只有复制, 两个阶段都需要进行复制,导致经过太多的RPC调用.
  • Multi Paxos 思想主要划分为两个Basic Paxos, 第一个Basic Paxos叫选举, 第二个Basic Paxos叫复制. 选举出一个leader需要通过一次Basic Paxos来达成共识, 通过引入leader 作为唯一的提案者去解决提案冲突问题, 然后所有的接受者以leader的提案为准, leader可以直接让接受者Acceptor去接受请求, 即不需要再经过一阶段准备阶段, 而是直接进入二阶段的提交阶段, 同时省略了一次状态复制的过程.

选举:

  • 接受者们在内部去发出的提案, 去投票选择谁才是leader, 选举协商的过程同样是一个Base Paxos模型(大致流程同上), 经过二阶段提交去选举出一个新的leader

复制

  • 当选举出leader后, 提议者将发出的提案都给到leader, 由leader一个人去决定是否通过提案. 当通过提案之后, 由leader去复制同步这个提案到其他的接受者直接去接受

在这里插入图片描述

10、打赏

如果觉得文章有用,你可鼓励下作者(支付宝)

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41347419/article/details/114647423