Seata1.2.0版本发布了一种新的交易模式:XA模式,支持XA协议。在这里,我们从三个方面来深入解读这个新特性:What:什么是XA模式?为什么:为什么支持XA?How:XA模式是如何实现的,如何使用?1、什么是XA模式?这里有两个基本的预先概念:什么是XA?Seata定义的所谓交易模式是什么?基于这两点,就很自然地理解了XA模式。1.1什么是XA?XA规范是由X/Open组织定义的分布式事务处理(DTP,DistributedTransactionProcessing)标准。XA规范描述了全局事务管理器和本地资源管理器之间的接口。XA规范的目的是允许在同一个事务中访问多个资源(如数据库、应用服务器、消息队列等),从而使ACID属性可以跨应用程序保持有效。XA规范使用两阶段提交(2PC,Two-PhaseCommit)来确保所有资源同时提交或回滚任何特定事务。XA规范是在1990年代初提出的。目前,几乎所有的主流数据库都支持XA规范。1.2Seata的交易模式是什么?Seata定义了全球交易的框架。一个全局事务定义为几个分支事务的整体协调:TM请求TC发起(Begin)、提交(Commit)、回滚(Rollback)全局事务。TM将代表全局事务的XID绑定到分支事务。RM向TC注册,将分支事务与XID代表的全局事务相关联。RM向TC报告分支事务的执行结果。(可选)TC向RM发送分支提交(BranchCommit)或分支回滚(BranchRollback)命令。Seata的全局事务处理过程分为两个阶段:执行阶段:执行分支事务,并保证执行结果是Rollbackable和Durable的。完成阶段:根据执行阶段的结果形成的决议,将TM发出的全局提交或回滚请求应用到TC,TC指示RM驱动分支事务进行提交或回滚。Seata所谓的事务模式是指分支事务运行在Seata全局事务框架下的行为模式。准确的说,应该叫分支交易模式。不同事务模式的区别在于分支事务使用不同的方法来实现全局事务两个阶段的目标。即回答以下两个问题:执行阶段:如何执行并确保执行结果是Rollbackable和Durable。完成阶段:收到TC命令后,如何提交或回滚分支?以Seata的AT模式和TCC模式为例来理解:AT模式执行阶段:回滚:根据SQL解析结果,记录回滚日志持久化:回滚日志和业务SQL在同一个本地事务中提交到数据库完成阶段:分支提交:异步删除回滚日志记录分支回滚:根据回滚日志进行反向补偿更新TCC模式执行阶段:调用业务定义的Try方法(回滚和持久化完全由业务层面保证)完成阶段:分支提交:调用各事务分支定义的Confirm方法分支回滚:调用各事务分支定义的Cancel方法1.3什么是SeataXA模式?XA模式:在Seata定义的分布式事务框架内,使用事务资源(数据库、消息服务等)支持XA协议,使用XA协议的机制来管理分支事务。执行阶段:回滚:业务SQL操作在XA分支进行,资源对XA协议的支持保证回滚持久化:XA分支完成后,执行XA准备。同样,资源对XA协议的支持,保证持久化(即任何意外都不会造成无法回滚的情况)完成阶段:分支提交:执行XA分支的commit分支回滚:执行该分支的rollback2XA分支。为什么支持XA?为什么在Seata中加入XA模式呢?支持XA有什么意义?2.1补偿交易模式问题从本质上讲,Seata已经支持的三大交易模式:AT、TCC、Saga都是补偿类型。补偿型事务处理机制是建立在事务资源之上的(无论是在中间件层面还是在应用层面),事务资源本身并不知道分布式事务。事务性资源对分布式事务不敏感有一个根本问题:它们无法实现真正??的全局一致性。例如,在补偿交易的过程中,库存记录从100条减少到50条。这时仓库管理员连接数据库,查询统计库存,看到当前是50,之后事务异常回滚,库存会补偿回滚到100。显然,仓库管理员查询统计的50是脏数据。可见,补偿分布式事务机制不需要事务资源本身(如数据库)机制的参与,因此无法在事务框架之外从全局角度保证数据的一致性。2.2XA的取值不同于CompensationType,XA协议需要TransactionResource本身提供规范和协议的支持。由于事务资源感知并参与分布式事务处理,因此事务资源(如数据库)可以保证从任何角度有效隔离数据访问,满足全局数据一致性。比如上一节提到的库存更新场景,在XA事务处理过程中,中间数据库存50是数据库自己保证的,不会被仓库管理员查询统计看到。(当然,隔离级别需要readandcommitted或以上。)除了全局一致性的基本价值外,支持XA还有以下好处:.给应用程序设计和开发带来额外负担。广泛的数据库支持:XA协议得到主流关系型数据库的广泛支持,无需额外适配即可使用。轻松的多语言支持:因为不涉及SQL解析,XA模式对Seata的RM要求更少,相对于AT模式开发不同语言的SDK会更薄更容易。传统基于XA的应用迁移:对于传统的基于XA的应用,使用XA模式迁移到Seata平台会更加顺畅。2.3XA受到广泛质疑。没有一种分布式事务机制可以完美适应所有场景,满足所有需求。XA规范早在1990年代初就被提出来解决分布式事务处理领域的问题。现在,无论是AT模式、TCC模式还是Saga模式,这些模式的提出,本质上都是源于XA规范无法满足某些场景的需求。XA规范定义的分布式事务处理机制存在一些广受质疑的问题。我们如何看待这些问题?1、数据加锁:数据被加锁直到整个事务处理过程结束,读写按照隔离级别的定义进行约束。思考:数据锁定是为更高的隔离性和全局一致性付出的代价。补偿型的事务处理机制在执行阶段完成分支(本地)事务的提交,不锁数据(资源层面)。这是以隔离为代价的。另外,AT模式使用全局锁来保证基本的写隔离。其实也是对数据进行加锁,只是锁在TC端集中管理,解锁效率高,不存在阻塞问题。2、协议阻塞:XAprepare后,分支事务进入阻塞阶段,必须阻塞等待,才能收到XAcommit或XArollback。思考:协议的阻塞机制本身不是问题,关键问题是协议阻塞遇到数据锁定。如果参与全局事务的资源“丢失”(无法接收到结束分支事务的命令),那么它锁定的数据将一直被锁定。反过来,它甚至可能导致死锁。这是XA协议的核心痛点,也是Seata在引入XA模式时将重点解决的问题。基本思路有两个方面:避免“失联”和增加“自解锁”机制。(这里涉及到很多技术细节,暂时不展开,在后续XA模式的演进中会具体讨论)3.性能不佳:性能损失主要来自两个方面:在一方面,交易协调过程增加单笔交易RT;另一方面,并??发事务数据锁冲突会降低吞吐量。思考:与不使用分布式事务支持的运行场景相比,性能肯定会下降,这是毋庸置疑的。本质上,事务(无论是本地事务还是分布式事务)机制都是牺牲部分性能来换取编程模型的简洁性。与同样是业务非侵入性的AT模式相比:首先,由于同样运行在Seata定义的分布式事务框架下,XA模式不会为事务协调产生更多的通信开销。其次,如果并发事务之间的数据存在热点,就会发生锁冲突。这种情况在AT模式下也存在(默认使用全局锁)。因此,在影响性能的两个主要方面,XA模式相比于AT模式并没有非常明显的劣势。AT模式的性能优势主要在于:集中管理全局数据锁??,锁的释放不需要RM的参与,锁的释放非常快;此外,全局提交的事务在完成阶段是异步的。3、如何实现和使用XA模式?3.1XA模式的设计3.1.1设计目标XA模式的基本设计目标主要有两个方面:从场景出发,满足全局一致性的要求。在应用方面,它与AT模式保持非侵入性一致。在机制上,适应了分布式微服务架构的特点。总体思路:与AT模式相同:在应用中以本地事务的粒度将分支事务构建成XA模式。通过数据源代理,将XA协议的交互机制封装在应用程序本地事务范围之外的框架层,XA编程模型透明。拆解XA的2PC,在分支事务执行阶段结束时进行XAprepare,将XA协议完美集成到Seata的事务框架中,减少一轮RPC交互。3.1.2核心设计1.整体运行机制XA模式运行在Seata定义的事务框架内:Execute:XAstart/XAend/XAprepare+SQL+registerbranchFinish:XAcommit/XArollback2。数据源代理XA模式需要XAConnection。XAConnection的获取方式有两种:方法一:需要开发者配置XADataSource方法二:基于开发者普通的DataSource创建第一种方法,增加了开发者的认知负担,需要XA模式专门学习和使用XA数据源,违背了透明XA编程模型的设计目标。第二种方式对开发者更友好。与AT模式的使用相同。开发者无需关心XA层面的任何问题,只需要保留本地编程模型即可。我们优先考虑第二种方式的设计和实现:数据源代理根据从公共数据源获取的公共JDBC连接创建对应的XAConnection。类AT模式的数据源代理机制如下:但是,第二种方式有局限性:不能保证兼容性的正确性。实际上,此方法正在执行数据库驱动程序将执行的操作。不同厂商和不同版本的数据库驱动实现机制是厂商专有的。我们只能保证驱动程序在经过全面测试的驱动程序上是正确的。开发者使用的驱动版本差异可能会导致机制失效。这在Oracle上非常明显。见Druidissue:https://github.com/alibaba/druid/issues/3707综合考虑,XA模式的数据源代理设计需要同时支持第一种方式:基于XA数据源的代理。类比AT模式下的数据源代理机制,如下:3.分支注册XA启动需要Xid参数。这个Xid需要和Seata全局事务的XID和BranchId关联起来,这样TC才能驱动XA分支的提交或者回滚。目前Seata的BranchId是在分支注册过程中由TC生成的,所以XA模式下分支注册的时机需要在XA启动之前。未来一个可能的优化方向:尽可能延迟分支注册。与AT模式类似,在本地事务提交之前注册分支,避免分支执行失败时无意义的分支注册。这个优化方向需要改变BranchId的生成机制。BranchId不是通过branch注册过程生成的,而是和BranchId一起生成的,用于注册branch。总结这里仅通过几个重要的核心设计来说明XA模式的基本工作机制。此外,还有连接维护、异常处理等重要方面,有兴趣的可以从项目代码中了解更多。以后我会继续写,和大家分享。3.1.3演化规划XA模型整体演化规划如下:Step1(完成):第一个版本(1.2.0),贯穿XA模型原型机制。确保只添加,不修改,不给其他模式引入新问题。第二步(计划5月完成):与AT模型进行必要的整合重构。第三步(计划7月完成):完善异常处理机制,对生产进行必要的打磨。第4步(计划于8月完成):性能优化。第5步(计划于2020年内完成):结合Seata项目正在进行的云原生TransactionMesh设计,创建云原生能力。3.2XA模式的使用从编程模型上看,XA模式与AT模式是完全一致的。可以参考Seata官网的示例:seata-xa示例场景是经典的Seata,涉及库存、订单、账户三个微服务的商品订购业务。示例中,上层编程模型与AT模型完全一致。只需要修改数据源代理就可以实现XA模式和AT模式的切换。@Bean("dataSourceProxy")publicDataSourcedataSource(DruidDataSourcedruidDataSource){//DataSourceProxyforATmode//returnnewDataSourceProxy(druidDataSource);//DataSourceProxyXAforXAmodereturnnewDataSourceProxyXA(druidDataSource);}满足所有场景的需求。不同的事务处理机制需要满足一致性、可靠性、易用性、性能和许多其他系统设计约束。Seata项目的核心价值在于:构建一个综合解决分布式事务问题的标准化平台。基于Seata,上层应用架构可以根据实际场景的需要,灵活选择合适的分布式事务方案。XA模式的加入填补了Seata在全局一致性场景下的空白,形成了AT、TCC、Saga、XA四大事务模式的布局,基本可以满足所有场景的分布式事务处理需求.当然,无论是XA模式,还是Seata项目本身,都不是完美的,还有很多需要改进和完善的地方。非常欢迎大家参与项目的建设,共同打造标准化的分布式交易平台。作者简介:炫梅,GitHubID:sharajava,阿里巴巴中间件GTS研发团队负责人,SEATA开源项目发起人,曾在甲骨文北京研发中心工作多年,一直从事WebLogic的核心研发工作。长期专注于中间件,尤其是分布式事务领域的技术实践。本文转载自微信公众号《高可用架构》,可通过以下二维码关注。转载本文请联系高可用架构公众号。
