分布式中的算法和协议:CAP、BASE、2PC、3PC、PAXOS


CAP原则

CAP原则又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者无法同时满足。

  • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本,与volatile关键字的可见性效果类似。)
  • 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
  • 分区容忍性(P):当节点间出现网络分区,照样可以提供服务。以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

分布式中的算法和协议:CAP、BASE、2PC、3PC、PAXOS

CAP原则是NOSQL数据库的基石。根据CAP原则,将NoSQL数据库分成了满足CA、CP或AP原则三大类:

  • CA:单点集群,满足一致性、可用性的系统,通常扩展性较差,甚至没办法部署子节点,这是违背分布式系统设计的初衷的。传统的关系型数据库RDBMS:Oracle、MySQL就是CA。
  • CP:满足一致性、分区容错性的系统,通常性能不是很高。相当于每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验
  • AP:满足可用性、分区容错性的系统,通常对一致性的要求低一些。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。



BASE原则

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的。

其核心思想是即使无法做到 强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到 最终一致性

  • 基本可用

基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。但绝不是指系统不可用。

例如响应时间上的损失:正常情况下,一个在线搜索引擎需要0.5秒内返回给用户相应的查询结果,但由于出现异常(比如系统部分机房发生断电或断网故障),查询结果的响应时间增加到了1~2秒。

  • 软状态

软状态也称为弱状态,和硬状态相对,是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。

  • 最终一致性

最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
分布式中的算法和协议:CAP、BASE、2PC、3PC、PAXOS



2PC(两阶段提交)

两阶段提交(Two Phase-Commit,2PC)是一种保证分布式系统数据一致性的协议,现在很多数据库都是采用的两阶段提交协议来完成 分布式事务 的处理。

分布式事务想要解决的问题是,在分布式系统中,整个调用链中,我们所有服务的数据处理要么都成功要么都失败,即所有服务的 原子性 问题。

在两阶段提交中,主要涉及到两个角色,分别是协调者(Coodinator)和参与者(Participant)。

  • 第一阶段

当要执行一个分布式事务的时候,事务发起者首先向协调者发起事务请求,然后协调者会给所有参与者发送 Prepare 请求(其中包括事务内容)告诉参与者你们需要执行事务了,如果能执行我发的事务内容那么就先执行但不提交,执行后请给我回复。然后参与者收到 Prepare 消息后,他们会开始执行事务(但不提交),并将 Undo 和 Redo 信息记入事务日志中,之后参与者就向协调者反馈是否准备好了。

  • 第二阶段

第二阶段主要是协调者根据参与者反馈的情况来决定接下来是否可以进行事务的提交操作,即提交事务或者回滚事务。

比如这个时候 所有的参与者 都返回了准备好了的消息,这个时候就进行事务的提交,协调者此时会给所有的参与者发送 Commit 请求 ,当参与者收到 Commit 请求的时候会执行前面执行的事务的 提交操作 ,提交完毕之后将给协调者发送提交成功的响应。

而如果在第一阶段并不是所有参与者都返回了准备好了的消息,那么此时协调者将会给所有参与者发送 回滚事务的 Rollback 请求,参与者收到之后将会 回滚它在第一阶段所做的事务处理 ,然后再将处理情况返回给协调者,最终协调者收到响应后便给事务发起者返回处理失败的结果。
分布式中的算法和协议:CAP、BASE、2PC、3PC、PAXOS

2PC 事实上只解决了各个事务的原子性问题,随之也带来了很多的问题。

  • 单点故障问题,如果协调者挂了那么整个系统都处于不可用的状态了。
  • 阻塞问题,即当协调者发送 prepare 请求,参与者收到之后如果能处理那么它将会进行事务的处理但并不提交,这个时候会一直占用着资源不释放,如果此时协调者挂了,那么这些资源都不会再释放了,这会极大影响性能。
  • 数据不一致问题,比如当第二阶段,协调者只发送了一部分的 commit 请求就挂了,那么也就意味着,收到消息的参与者会进行事务的提交,而后面没收到的则不会进行事务提交,那么这时候就会产生数据不一致性问题。



3PC(三阶段提交)

因为2PC存在的一系列问题,比如单点,容错机制缺陷等等,从而产生了 3PC(三阶段提交)。3PC 在很多地方进行了超时中断的处理。

  1. CanCommit阶段:协调者向所有参与者发送 CanCommit 请求,参与者收到请求后会根据自身情况查看是否能执行事务,如果可以则返回 YES 响应并进入预备状态,否则返回 NO 。
  2. PreCommit阶段:协调者根据参与者返回的响应来决定是否可以进行下面的 PreCommit 操作。如果上面参与者返回的都是 YES,那么协调者将向所有参与者发送 PreCommit 预提交请求,参与者收到预提交请求后,会进行事务的执行操作,并将 Undo 和 Redo 信息写入事务日志中 ,最后如果参与者顺利执行了事务则给协调者返回成功的响应。如果在第一阶段协调者收到了 任何一个 NO 的信息,或者 在一定时间内 并没有收到全部的参与者的响应,那么就会中断事务,它会向所有参与者发送中断请求(abort),参与者收到中断请求之后会立即中断事务,或者在一定时间内没有收到协调者的请求,它也会中断事务。
  3. DoCommit阶段:这个阶段其实和 2PC 的第二阶段差不多,如果协调者收到了所有参与者在 PreCommit 阶段的 YES 响应,那么协调者将会给所有参与者发送 DoCommit 请求,参与者收到 DoCommit 请求后则会进行事务的提交工作,完成后则会给协调者返回响应,协调者收到所有参与者返回的事务提交成功的响应之后则完成事务。若协调者在 PreCommit 阶段 收到了任何一个 NO 或者在一定时间内没有收到所有参与者的响应 ,那么就会进行中断请求的发送,参与者收到中断请求后则会 通过上面记录的回滚日志来进行事务的回滚操作,并向协调者反馈回滚状况,协调者收到参与者返回的消息后,中断事务。
    分布式中的算法和协议:CAP、BASE、2PC、3PC、PAXOS

3PC 在很多地方进行了超时中断的处理,比如协调者在指定时间内为收到全部的确认消息则进行事务中断的处理,这样能减少同步阻塞的时间 。还有需要注意的是,3PC 在 DoCommit 阶段 参与者如未收到协调者发送的提交事务的请求,它会 在一定时间内进行事务的提交。为什么这么做呢?是因为这个时候我们肯定保证了在第一阶段所有的协调者全部返回了可以执行事务的响应,这个时候我们有理由相信其他系统都能进行事务的执行和提交,所以不管协调者有没有发消息给参与者,进入第三阶段参与者都会进行事务的提交操作。

3PC 通过一系列的超时机制很好的缓解了阻塞问题,但是最重要的一致性并没有得到根本的解决。比如在 PreCommit 阶段,当一个参与者收到了请求之后其他参与者和协调者挂了或者出现了网络分区,这个时候收到消息的参与者都会进行事务提交,这就会出现数据不一致性问题。



Paxos 算法

Paxos 算法是基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一,其解决的问题就是在分布式系统中如何就某个值(决议)达成一致 。

在 Paxos 中主要有三个角色,分别为 Proposer提案者、Acceptor表决者、Learner学习者。Paxos 算法和 2PC 一样,也有两个阶段,分别为 Prepare 和 Accept 阶段。

  • Prepare 阶段

Proposer提案者:负责提出提案(Proposal),每个提案者在提出提案时都会首先获取到一个具有 全局唯一性 的、递增 的提案编号N,即在整个集群中是唯一的编号 N,然后将该编号赋予其要提出的提案,在第一阶段是只将提案编号发送给所有的表决者。

Acceptor表决者:每个表决者在 Accept 某提案编号后,会将该提案编号N记录在本地,这样每个表决者中保存的已经被 Accept 的提案中会存在一个编号最大的提案,其编号假设为 maxN。每个表决者仅会 Accept 编号大于自己本地 maxN 的提案,在批准提案时表决者会将以前接受过的最大编号的提案作为响应反馈给 Proposer 。

  • Accept 阶段

当一个提案被 Proposer 提出后,如果 Proposer 收到了超过半数的 Acceptor 的批准(Proposer 本身同意),那么此时 Proposer 会给所有的 Acceptor 发送真正的提案(你可以理解为第一阶段为 试探以及更新标识编号 ),这个时候 Proposer 就会发送提案的内容和提案编号。

表决者收到提案请求后会再次比较本身已经批准过的最大提案编号和该提案编号,如果该提案编号 大于等于 已经批准过的最大提案编号,那么就 Accept 该提案(此时执行提案内容但不提交),随后将情况返回给 Proposer 。如果不满足则不回应或者返回 NO 。

当 Proposer 收到超过半数的 Accept ,那么它这个时候会向所有的 Acceptor 发送提案的提交请求。需要注意的是,因为上述仅仅是超过半数的 Acceptor 批准执行了该提案内容,其他没有批准的并没有执行该提案内容,所以这个时候需要向未批准的 Acceptor 发送提案内容和提案编号并让它无条件执行和提交,而对于前面已经批准过该提案的 Acceptor 来说 仅仅需要发送该提案的编号 ,让 Acceptor 执行提交就行了。

而如果 Proposer 如果没有收到超过半数的 Accept 那么它将会将该 Proposal 的编号 递增 ,然后 重新进入 Prepare 阶段 。
分布式中的算法和协议:CAP、BASE、2PC、3PC、PAXOS

  • Proposer、Acceptor和 Learner 三者的关系

Proposer、Acceptor 以及 Learner 都是逻辑上的概念,区分出不同的角色是因为他们的职责不同。

  • Proposer 负责提议
  • Acceptor 负责就某个提议达成决议
  • Learner 负责获取已经达成决议的值

在Paxos算法的具体使用场景中,被选出的值最终会应用到复制状态机,也就是在应用层生效。Learner正是负责感知被选出的决议并最终促使该决议Apply到复制状态机,然后存储提案

在具体的实践中,每个物理节点会同时担任多个角色

  • Leader:同时担任Proposer、Acceptor以及Learner
  • Follower:同时担任Acceptor以及Learner

没有Learner,就会成为薛定谔的Paxos,提议是否最终达成一致是不一定的。

  • Paxos 算法的死循环问题

Paxos 算法会出现死循环问题,即提案因为步骤中通过的不同时效性导致无限提案的情况。这个问题通过只允许一个提案者能提案可以解决。

举个例子,此时提案者 P1 提出一个方案 M1,完成了 Prepare 阶段的工作,这个时候 acceptor 则批准了 M1,但是此时提案者 P2 同时也提出了一个方案 M2,它也完成了 Prepare 阶段的工作。然后 P1 的方案已经不能在第二阶段被批准了(因为 acceptor 已经批准了比 M1 更大的 M2),所以 P1 自增方案变为 M3 重新进入 Prepare 阶段,然后 acceptor ,又批准了新的 M3 方案,它又不能批准 M2 了,这个时候 M2 又自增进入 Prepare 阶段。
分布式中的算法和协议:CAP、BASE、2PC、3PC、PAXOS


本文参考:
[1] @duanxz 的博客园:CAP原则(CAP定理)、BASE理论
[2] @JavaGuide:Zookeeper 原理简单入门
[3] @无名 在知乎的回答:paxos算法中learner是必须存在的吗?如果是的话那必要性是什么?

相关文章: