共识协议:两阶段提交

问题

       日常我们都有银行转账的经历,一个用户A银行X的账户上转了100元用户B银行Y的账户。如果单独分析用户A用户B在各自银行账户的金额变化,它们就是独立的本地事务,但是由于这个场景是跨多个数据源,所以它是一个分布式事务场景,而分布式事务代表的是全局事务。

       如上图所示,用户A银行X账户的扣款以及用户B银行Y账户的存款是一个全局事务,它也符合事务的ACID原则,尤其是要兑现原子性(Atomicity),也就是要么这两个操作全部成功,要么全部失败,没有中间状态。如果把这两个操作视作两个节点,在转账场景中,它们就形成了一个更大范畴的分布式系统(虽然银行X和Y的系统并不相同),而要确保分布式事务的原子性,需要保证:

       (1)安全性(Safety),所有节点共进退,要么成功,要么失败;

       (2)存活性(Liveness),所有节点正常,则成功。可以允许有异常节点,但最终需要有一个一致结果,且不能一直等待。

       参与分布式事务的多个节点需要对事务的提交或回滚达成一致,其实事务的提交与否和就一个值多节点达成一致,在本质上是同一回事,这就涉及到了分布式一致性问题。根据CAP原理,由于分区已经客观存在,保证安全性和存活性的必要条件就是保证一致性(Consistency),确保分区环境下的一致性的协议有许多,常见的就是两阶段提交(Two Phase Commit)。

协议

       两阶段提交协议为了为了保持跨多个节点全局事务的ACID特性,通过引入一个协调者组件来统一掌控所有节点(或称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。该协议的算法思路可以概括为:协调者先问询所有参与者的处理意见,参与者将结果通知到协调者;再由协调者根据所有反馈结果决定各参与者接下来是提交还是中止。

       两阶段提交涉及到的角色一般有三个:应用程序协调者(或称作事务管理器)参与者(或称作资源管理器)。由应用程序发起操作,协调者一般是独立部署的中间件,而参与者是关系型数据库,协调者参与者之间通过XA协议进行沟通。两阶段分为:准备阶段和提交阶段,分别应对投票和执行两个场景,其中准备阶段如下图所示:

       准备阶段是由应用程序发起,通过协调者将提交事务的请求发送给所有参与者参与者收到请求后在本地记录日志并将处理结果返回给协调者,然后等待最终命令,此时不做提交,对于外部数据变更是不可见的。处理结果只有两种,同意或中止。该阶段完成后,整体事务进入提交阶段,如下图所示:

       协调者根据准备阶段收集到各参与者返回的处理结果集合进行判定,如果全部为同意,则向所有参与者发起提交命令,如果存在中止,则向所有参与者发起中止命令,当所有参与者响应命令后,整体事务完成。

       可以看到两阶段提交协议是一种非常朴素的分布式一致性协议,依靠协调者来协同各参与者,确保不同参与者的状态一致。在安全性上能够确保所有节点有一致的意愿,但是在存活性上是存在一些问题的,比如:协调者在事务执行中突然崩溃导致参与者出现悬停,接下来我们运用CAP原理拜占庭将军问题分析一下二阶段提交的一些问题。

分析

       CAP原理指出,在一个分区的(分布式)系统中,无法同时满足一致性和可用性。两阶段提交协议面对分区选择了强一致性,因此在可用性上就会存在挑战和问题。选择强一致性,势必会在多个分区(或系统)之间同时锁定更多的资源,由于网络通信的不确定性,从而导致可用性降低。实际上二阶段提交最大的缺点就在于在执行时,参与节点都处于阻塞状态,节点之间相互等待对方的响应消息,而一旦某个节点锁定了某些资源后,其他(或非本事务)节点访问这些资源也会陷入阻塞状态,当然这也是保障一致性(隐性包含了可见性)的代价。

       如果我们将拜占庭将军问题的拓扑模型来演绎二阶段提交的执行场景,可以看出在该场景中,协调者扮演的是指挥官,而每个参与者就是中尉协调者下达命令,参与者执行后反馈。由于各方通过消息(或者远程调用)进行沟通,而要形成共识,就需要对假设1-3能够满足,前两个假设比较容易解决,而两阶段提交面对的问题就在假设3的兑现上,也就是如何能发现对方(协调者参与者)的消息缺失。

       从协调者的角度去看,在准备阶段发起对各个参与者的请求后,就需要等待所有参与者的响应,如果某一个参与者未响应或者响应消息丢失,则假设3无法满足,无法达成共识,进而无法满足一致性。二阶段提交解决该问题的方式是增加协调者自身的超时机制,如果超时时间到达后,存在参与者没有响应,则通知所有参与者进行中止操作。

       从参与者的角度去看,在提交阶段等待参与者的最终命令时,如果协调者此时出现故障导致未发送命令或者命令消息丢失,则也会导致一致性无法被满足。参与者也需要超时机制,在超时时间到达后,不能简单的做出提交或者中止的操作,而是需要同各个参与者进行协商,具体问题具体分析。比如:协调者可能没有发出命令,或者协调者可能发出了提交的命令,已经触达到了部分参与者后出现了问题。由于各个参与者不知道其他参与者在准备阶段的响应结果,所以就需要通信协商,而一旦涉及到参与者之间的通信,就会使这个问题变得更加复杂,最终很难有一个好的解决方案。

       可以看出两阶段提交通过引入协调者并利用准备和提交两个阶段简单的解决了分布式一致性问题,但在实际情况中,它却存在不少问题。两阶段提交要求对各参与者的资源占用时间横跨两个阶段,对资源占用时间过长导致吞吐量低,并且由于协调者(或部分参与者)稳定性或消息丢失问题,使得一旦出现问题,很难保证事务的一致性,只能提升协调者的可用性来减少问题的出现。

results matching ""

    No results matching ""