来源:后端开发技术
继续分布式事务专题:本文讲解的是强一致性解决方案XA、2PC、3PC。
接着上一篇文章,由于我们非常关注数据的一致性,所以总体来说按照一致性强弱的维度分类,解决分布式事务问题可以有以下方案:
本文我们讲解强一致性方案:XA协议,2PC(两阶段提交)、3PC(三阶段提交)。如果上一篇基础内容还没有读,请移步。

CAP、BASE理论真的很重要!|分布式事务系列(一)
因为在使用本地事务的过程中有数据裤引擎的保证,但是如果是多数据源场景下就有了一致性问题,为此产生了全局事务。全局事务并不限定数据源是一个还是多个,但是在分布式系统场景下我们都当作多数据源的分布式事务来讨论。
为了解决分布式事务的一致性问题、统一标准,1991 年X/Open组织(后来并入国际开放标准组织The Open Group )提出了一套名为X/Open XA(XA 是 eXtended Architecture 的缩写)的处理事务架构,制定了标准化的模型和接口。它定义了全局的事务管理器(Transaction Manager,用于协调全局事务)和局部的资源管理器(Resource Manager,用于驱动本地事务)之间的通信接口。1994年国际开放标准组织The Open Group 在1994年定义了分布式事务处理模型 DTP(Distributed Transaction Processing Reference Model,X/Open XA),XA 协议成为事务模型事实上的标准。
在 XA 协议下有三种核心角色:

基于 XA 规范 Java 中实现了 JTA ,MySQL、Oracle 也都对其做了支持,XA约定了TM和RM之间双向通讯的接口规范,能在一个TM和多个RM之间形成通信桥梁,通过协调多个数据源的一致动作,实现全局事务的统一提交或者统一回滚。
在XA协议下如果我们想实现一个商城的下单、扣款、扣库存功能,可以实现如下伪代码。
orderTransaction.begin();//订单
balanceTransaction.begin();//余额
warehouseTransaction.begin();//仓库
try{
order.submit();//下单
balance.pay();//付款
warehouse.decrease();//扣减库存
orderTransaction.commit();//订单
balanceTransaction.commit();//余额
//故障点
warehouseTransaction.commit();//仓库
}catch(Exception e){
//无法回滚
orderTransaction.rollback();//订单
balanceTransaction.rollback();//余额
warehouseTransaction.rollback();//仓库
}
订单、余额、仓库三个数据源分别开启事务,如果成功分别提交事务。但是这段逻辑有个很大的问题就是如果订单、余额事务提交成功,但是仓库commit发生异常,为了保证一致性需要全部回归,但是由于订单、余额已提交所以无法回滚,发生了数据不一致场景。面对这种问题,XA协议选择了两阶段提交(2PC)作为实现,从而在多个数据库资源下保证 ACID 四个特性。
关于两阶段的内容我们在这里不做过多解释,具体请看后面两阶段专题。
由于 XA 使用两阶段提交,因此两阶段提交的优点和缺点通常适用于 XA。
优点:XA 允许跨多种异构技术的原子事务(不同类型数据源、消息中间件),解决了分布式事务的问题,而传统数据库事务仅限于单一数据库。
缺点:准备阶段会长时间持有资源,造成系统性能降低,并且协调者会有单点故障的问题。
如果你看到这感觉晕晕乎乎,还是没明白XA到底是个啥,那需要看一下这段总结。
XA 协议是一种用于处理分布式事务的协议,定义了TM、RM、应用程序集中模型和接口,大多数实现XA的都是一些关系型数据库(包括MySQL,SQL Server、PostgreSQL 和Oracle)和消息中间件(包括ActiveMQ,HornetQ,MSMQ和IBM MQ),Java 中的 JTA 也实现了 XA 规范接口。
两阶段提交(Two-Phase Commit,简称2PC)是XA协议中的一种实现方式。通过接口规范,应用程序访问并使用RM的资源,并通过TM的事务接口(TX interface)定义需要执行的事务操作,然后 TM和 RM 会基于 XA 规范,执行二阶段提交协议进行事务的提交/回滚。
阶段提交(Two-Phase Commit,简称2PC)是分布式事务处理中的一种算法,是最为经典的分布式事务解决方案之一。用于确保在涉及多个节点(或进程)的事务中,所有节点要么全部提交(commit),要么全部回滚(rollback),从而保持数据的一致性。
为了协调分布式环境下的不同服务,它通过一个中心协调器来协调多个参与者的事务。该协调器在第一阶段(准备阶段)询问所有参与者是否可以提交事务,如果所有参与者都准备好了,则在第二阶段(提交阶段)通知所有参与者提交事务。
两阶段提交算法是由Jim Gray和Andreas Reuter在1981年提出的,并在1983年发表了相关论文《事务处理:概念和技术》(Transaction Processing: Concepts and Techniques)中进行了详细讨论。这篇论文是分布式事务处理领域的经典著作之一,对于今天的分布式系统设计仍然具有重要的参考价值。
两阶段指的是分布式事务的提交过程分为两个阶段,具体流程如下:
第一阶段(投票阶段):
第二阶段(提交阶段):


需要注意的是,两阶段提交协议有可能会存在“阻塞”的问题,也就是说,在第一阶段中,如果有任何一个参与者无法响应,那么协调者将一直等待,直到超时。在这种情况下,需要采取超时机制和其他优化手段来提高系统的可用性和性能。
因为它需要在所有两阶段的缺点是存在单点故障和阻塞问题。
两阶段提交的成立是有前提条件的。
虽然两阶段可以解决大部分场景下的事务的一致性问题,并且原理简单,但是它存在以下一些缺点。
1.资源占用导致同步阻塞问题:在第一阶段投票之后,参与的事务的每个节点资源都被锁定,并且在第二阶段参与者等到协调者的响应才能继续执行,事务时间越长资源占用时间越长。并且如果协调者发生故障,参与者会一直等待其响应,这回导致整个系统的性能受到影响。
2.单点故障问题:在2PC中,协调者扮演着关键的角色,如果协调者发生故障,整个事务就会失败。协调者等待参与者回复时可以有超时机制,允许参与者宕机,但参与者等待协调者指令时无法做超时处理,事务将长时间持续。因此,协调者成为了系统的单点故障,这会影响整个系统的可用性。
3.数据不一致问题:其实两阶段提交有个前提,就是网络状况短时间内稳定,这样可以保证第一阶段投票完毕后第二个阶段可以顺利提交。并且必须假设因故障而下线的节点最终能宕机恢复,在第一阶段中可以有 Undo Log 可以保证。但是如果在第一阶段之后,协调者向参与者发送的提交请求丢失或者超时或者节点宕机,那么就会导致一些参与者已经提交了数据,而另外一些参与者却没有提交数据,从而导致数据不一致。

4.可扩展性问题:在大规模分布式系统中,参与者数量可能会非常庞大,这会导致协调者需要维护大量的状态信息,从而影响系统的可扩展性。
三阶段提交(Three-Phase Commit,简称3PC)是分布式事务处理中的一种算法,是在两阶段提交(2PC)的基础上进一步发展的,主要是为了解决2PC中存在的一些问题,比如资源占用、同步阻塞、单点故障以及提交阶段可能出现的数据不一致问题。
相对于2PC,3PC 有两个变动的地方:
3阶段提交的具体过程如下:

第一阶段:准备阶段(CanCommit)
事务协调者向所有参与者发送准备请求,并询问参与者是否可以提交事务,并开始等待各参与者响应。参与者接收到请求后,会查询本地资源是否可以提交,并返回查询结果,而不需要对资源进行实际的修改操作。如果查询资源可以提交,则回复事务管理器“可以提交(Yes)”,否则回复“不可以提交(No)”。
和2PC的第一阶段不同的是,这里只执行检查,并不锁定资源。
第二阶段:预提交阶段(PreCommit)
协调者根据第一阶段参与者返回的结果,会出现两种情况:执行事务预提交和中断事务。
情况一,执行事务预提交:
情况二,中断事务:
第三阶段:确认阶段(DoCommit)
根据第二阶段的结果,也可以分为两种情况,提交事务和中断事务。
情况一,提交事务:
情况二,中断事务
3PC解决了2PC存在的以下问题:
只能说3PC对2PC存在的一些问题有改善,但没有彻底解决。
比如它依旧存在一致性风险问题,并且风险反而略有增加。进入 PreCommit 阶段之后,协调者发出的指令不是 Ack 而是 Abort,而此时因网络问题,有部分参与者直至超时都未能收到协调者的 Abort 指令的话,这些参与者将会错误地提交事务,这就产生了不同参与者之间数据不一致的问题。
由于3PC非常难实现,目前市面上主流的分布式事务解决方案都是2PC协议。所以,在实际应用中需要根据具体的场景和需求,选择适合的分布式事务协议。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70027824/viewspace-2942100/ ,如需转载,请注明出处,否则将追究法律责任。