@[toc]分布式事务系列继续!之前宋哥跟大家聊过Seata中的TCC模式和AT模式。没看过的可以先看看:五分钟带你体验一个分布式事务!太简单!看了那么多博客,还是不太懂TCC,那我们就来看看这个案例吧!今天继续学习XA事务!Seata支持四种不同的事务模式:AT、TCC、XA和Saga。在这四种不同的交易模式中,XA是最具特色的一种!为什么这么说,相信看完这篇文章你就明白了。1.什么是XA规范1.1什么是两阶段提交我们先来回顾一下两阶段提交。先来看下图:这张图涉及到三个概念:AP:不用说了,AP就是应用本身。RM:RM是资源管理器,即事务的参与者。在大多数情况下,它指的是数据库。一个分布式事务往往涉及多个RM。TM:TM是事务管理器,创建分布式事务,协调分布式事务中各个子事务的执行和状态。子事务是指对RM进行的具体操作。那么什么是两阶段(Two-PhaseCommit,简称2PC)提交呢?说白了,两阶段提交很简单。宋大哥给大家举个简单的例子来解释一下两阶段提交:比如下图(带你五分钟体验一个分布式事务!soeasy!你在文章里见过这张图):带你五分钟体验分布式事务!太简单!在第一篇中,我们在Business中分别调用了Storage、Order、Account。这三个中的操作必须同时成功或失败。但是,由于这三个点在不同的服务中,我们只能让三个服务中的操作分别执行,三个服务中的事务分别执行,这是两个阶段中的第一阶段。第一阶段执行完后,不要急于提交,因为这三个服务有可能执行失败。这时候三个服务都需要将各自阶段的执行结果上报给一个事务协调器(也就是上一篇文章SeataServer中的SeataServer),事务协调器收到消息后,如果第一阶段的三个服务执行成功,则通知三个事务分别提交,如果三个服务中有任何一个执行失败,则通知三个事务分别回滚。这就是所谓的两阶段提交。总结一下:在两阶段提交中,事务分为参与者(如上图中的具体服务)和协调者(上例中的SeataServer),参与者会通知协调者成功或操作失败,然后协调器根据所有参与者的反馈信息来决定每个参与者是提交操作还是中止操作。这里的参与者可以理解为RM,协调者可以理解为TM。但是Seata中的各种分布式事务模式基本都是在两阶段提交的基础上演化而来的,所以并不完全相同,需要小伙伴们注意。1.2什么是XA规范XA规范是由X/Open组织定义的分布式事务处理(DTP,DistributedTransactionProcessing)标准。XA规范描述了全局事务管理器和本地资源管理器之间的接口。XA规范的目的是允许在同一个事务中访问多个资源(如数据库、应用服务器、消息队列等),从而使ACID属性可以跨应用程序保持有效。XA规范使用两阶段提交来保证所有资源同时提交或回滚任何特定事务。XA规范是在1990年代初提出的。目前,几乎所有的主流数据库都支持XA规范。XA事务的基础是两阶段提交协议。需要交易协调员来确保所有交易参与者都准备就绪(阶段1)。如果协调器收到所有参与者都准备就绪的消息,它会通知所有事务它们已准备好提交(阶段2)。MySQL在这个XA事务中扮演的是参与者的角色,而不是协调者(事务管理器)。MySQL的XA事务分为内部XA和外部XA。外部XA可以参与外部分布式事务,这需要应用层作为协调者的介入;内部XA事务用于同一实例下的跨引擎事务,Binlog作为协调器。比如存储引擎commit时,需要将提交的信息写入binarylog,这是一个分布式的内部XA事务,但是binarylog的参与者是MySQL本身。MySQL在XA事务中扮演参与者的角色,而不是协调者。2.MySQL中的XA接下来宋哥将通过一个简单的例子来向大家展示MySQL中的XA是如何工作的。2.1两阶段交易提交比如我们上面的转账操作,我用MySQL中的XA交易给大家演示了从一个账户转账10元:上面的交易提交就是一个两阶段交易提交的案例。具体执行步骤如下:XASTART"transfer_money":表示启动一个XA交易,后面的字符串是交易的xid,是一个唯一的字符串。启动后,事务状态变为ACTIVE。更新帐户集amount=amount-10whereaccount_no='A';这意味着执行特定的SQL。XAEND"transfer_money":表示一次XA交易结束,此时交易状态变为IDLE。XAPREPARE“transfer_money”:这将交易置于PREPARE状态。XACOMMIT"transfer_money":这用于提交交易。提交后,事务的状态为COMMITED。最后一步可以通过XACOMMIT提交,也可以通过XAROLLBACK回滚。回滚后事务的状态为ROLLBACK。另外,第四步可以省略,即一个IDLE状态的XA事务可以直接提交或者回滚。我们来看下面的流程图:从这张图中我们可以看出事务可以一步提交,也可以分两阶段提交,这两种方式都是支持的。如果是两阶段提交,prepare之后,其实是在等待其他资源管理器(RM)反馈结果。2.2直接提交交易。宋哥教你如何一步提交交易:这个比较简单,没什么好说的。本节介绍另外一个XA事务相关的命令XARECOVER,如下图:XARECOVER可以列出所有处于PREPARE状态的XA事务,其他状态的事务则不会列出,如图多于。2.3总结在客户端环境中,XA事务和本地(非XA)事务是相互排斥的。如果已经通过XASTART开启了一个事务,那么在XA事务提交或者回滚之前,本地事务不会被启动。.反之,如果本地事务已经用STARTTRANSACTION启动,则在事务提交或回滚之前不能使用XA语句,XA事务只有InnoDB存储引擎支持。3.Seata3.1中的XA我们先简单了解一下Seata中的XA模式,然后在3.2节中我们再来看代码实践。通过上面的介绍,大家已经知道MySQL中的XA事务是怎么回事了。Seata中的XA模式实际上是在MySQL中的XA模式的基础上实现的。Seata中的XA模式是在Seata定义的分布式事务框架内,使用事务性资源(数据库、消息服务等)来支持XA协议,利用XA协议的机制来管理分支事务。我们看下图:先简单说一下执行步骤:首先,通过TM启动全局分布式事务。每个业务SQL放在不同的XA分支中。具体执行流程为XAStart->BusinessSQL->XAEnd。这个过程和我在2.1节中演示的MySQL中XA事务的过程是一致的。分支中的XA事务执行完成后,执行XAprepare,将自己执行的状态报告给TC。其他分支事务按照步骤2、3执行。当所有分支事务都执行完毕后,TC也会收到各个分支事务上报的执行状态。如果所有状态都OK,则TC通知所有RM执行XACommit完成事务的最终提交,否则TC通知所有RM执行XARollback进行一次事务回滚。这是Seata中的XA模式!小伙伴们只要了解了2.2节中MySQL的XA模式,那么Seata中的XA模式就很容易理解了。3.2代码实践接下来我们来看一下代码实践,通过一个具体的案例来看一下Seata中的XA模式。这里我们还是使用官方的案例。case的业务和AT的业务一样,也是订单case。如果您对本案业务不熟悉,可以花五分钟看一看,带您体验分发。正式事务!太简单!一篇文章。但是之前说过,官方案例中有很多依赖,下载容易出错,而且有些依赖有版本冲突,需要自己解决才能运行,所以宋哥也整理了一套在这里,如下:朋友们可以在公众号后台回复seata-demo下载本案例,下载后直接运行即可。这里是案例代码的问题,我们还需要提前准备好数据库。首先可以看到案例中有一个sql目录,这个目录下有对应的数据库脚本all_in_one.sql。我们在数据库中创建一个名为seata_xa的数据库,然后在数据库中执行脚本。执行完成后会生成三张表:与AT模式相比,这里少了一张undo_log表。原因很简单。AT两阶段回滚使用反向补偿(通过update语句恢复数据),而XA使用数据库自??带的XA模式通过XAROLLBACK命令回滚,所以XA模式不需要undo_log表。接下来分别修改account-xa、order-xa、storage-xa、business-xa模块的application.properties配置文件,更改数据库连接地址,如下:spring.datasource.url=jdbc:mysql:///seata_xa?useSSL=false&serverTimezone=UTCspring.datasource.username=rootspring.datasource.password=123修改完成后,准??备工作完成,接下来就是启动项目(business-xa会自动完成启动成功后的数据初始化))。首先,您需要确保您的seata-server已启动。seata-server的启动也需要eureka的支持。详细可以参考五分钟带你体验分布式事务!太简单!一篇文章。然后分别启动account-xa、order-xa、storage-xa和business-xa模块。项目启动成功后,再访问以下地址完成一次购买操作:http://localhost:8084/purchase这是一次成功的购买操作,即第二阶段的交易会被提交而不是回滚。访问后,浏览器响应如下:接下来,我们可以添加一个访问参数count,表示购买的商品数量。这里要购买的商品数量设置为999(实际没有那么多商品),所以在第二阶段事务提交的时候会回滚。访问http://localhost:8084/purchase?count=999后,浏览器响应如下:ThisistoprocessdistributedtransactionsbySeataXAmode.3.3代码分析有些朋友可能会觉得XA和AT很像,在代码层面确实很像!但原理还远远落后。SeataXA模式与SeataAT模式相比,主要是修改数据源。可以注意到account-xa、order-xa、storage-xa这三个微服务的数据源都是DataSourceProxyXA。以order-xa模式为例,数据源配置如下:}@Bean("dataSourceProxy")publicDataSourcedataSource(DruidDataSource){//AT模式的DataSourceProxy//returnnewDataSourceProxy(druidDataSource);//XA模式的DataSourceProxyXAreturnnewDataSourceProxyXA(druidDataSource);}@Bean("jdbcTemplate")publicJdbcTemplatejdbcTemplate(DataSourcenewdataSourceProxy){JdbcTemplate(DataSourcenewdataSourceProxy){JdbcTemplatedataSourceProxy);}}可以看到,这里使用的数据源不是原始数据源,而是封装后的DataSourceProxyXA。如果仅从代码层面来说,用DataSourceProxy替换DataSourceProxyXA就是AT模式,用DataSourceProxyXA替换DataSourceProxy就是XA模式,就这么简单!本例其他地方的代码与AT模式基本相同,这里不再赘述。不过需要注意一点,business-xa模块的数据源并没有使用DataSourceProxyXA。原因是该模块中数据源的配置主要是初始化数据,不涉及分布式事务。另外需要注意的是,在每一个分支事务中,都需要通过@Transactional注解开启事务。开启后会遵循XA集,否则出错不会回滚服务。默认情况下,只有account-这个注解被添加到xa模块中。在具体的测试过程中,也可以在其他模块中添加这个注解,测试一下效果。4、XA的几个问题XA模式有几个被广为诟病的问题,一起来一探究竟吧。数据锁定在使用XA事务时,数据在整个事务过程结束前被锁定,根据隔离级别的定义限制读写。这确实是XA模式的劣势,但也是为更高的隔离性和全局一致性付出的代价。之前松哥分享的补偿性事务处理机制(AT、TCC)虽然没有这个问题,但是牺牲了隔离性。AT模式使用全局锁来确保基本的写隔离。其实它也是对数据进行加锁,只是锁在TC端集中管理,解锁效率高,不存在阻塞问题。协议阻塞XAprepare后,分支事务进入阻塞阶段,必须阻塞等待,才能收到XAcommit或XArollback。阻塞机制本身没有问题。关键问题是协议阻塞遇到数据锁定。如果参与全局事务的资源“断开”(无法接收到结束分支事务的命令),则其锁定的数据将一直处于锁定状态。锁定,这可能会导致死锁。这是XA协议的核心痛点,也是Seata在引入XA模式时需要重点关注的问题。Seata主要解决掉线问题,通过增加自解锁机制来解决这个问题。性能不佳这可能是最受诟病的地方。XA模式的性能损失主要来自两个方面:一方面,事务协调过程增加了单个事务的RT;另一方面,并??发事务数据的锁冲突降低了吞吐量。与不使用分布式事务支持的运行场景相比,性能肯定是有所下降的,这是毋庸置疑的。本质上,事务(无论是本地事务还是分布式事务)机制都是牺牲一些性能来换取简单的编程模型。与同样对业务无侵入的AT模式相比:同样运行在Seata定义的分布式事务框架下,XA模式不会产生更多的事务协调通信开销。并发事务之间,如果数据存在热点,就会发生锁冲突。这种情况在AT模式下也存在(默认使用全局锁)。因此,在影响性能的两个主要方面,XA模式相比于AT模式并没有非常明显的劣势。AT模式的性能优势主要在于:集中管理全局数据锁??,锁的释放不需要RM的参与,锁的释放非常快;此外,全局提交的交易在完成阶段是异步的。5.小结在技术发展的现阶段,还没有一种分布式事务处理机制能够完美满足所有场景的需求。不同的事务处理机制需要满足一致性、可靠性、易用性、性能和许多其他系统设计约束。Seata项目的核心价值在于:构建一个综合解决分布式事务问题的标准化平台。基于Seata,上层应用架构可以根据实际场景的需要,灵活选择合适的分布式事务方案。参考资料:https://seata.io/zh-cn/blog/s...https://www.cnblogs.com/duyan...
