分布式事务是分布式系统开发中的一大挑战,XA 协议作为经典的强一致性解决方案,基于 两阶段提交(2PC) 协议来保证所有事务参与者要么全部成功,要么全部失败。本文将深入剖析 XA 协议的原理,并通过 Seata 框架实现 XA 模式的实际应用。
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统节点上。XA是由X/Open组织提出的分布式事务处理(DTP,Distributed Transaction Processing)规范,旨在保证分布式事务的强一致性(ACID原则),几乎所有主流数据库(如MySQL、Oracle等)都对XA规范提供了支持。
XA协议通过 两阶段提交(2PC) 机制,协调多个 资源管理器(RM),确保在分布式事务中,所有参与者要么全部提交,要么全部回滚。
在DTP模型中,核心组件包括:
XA 协议的核心是两阶段提交(2PC),其基本思想是将事务的提交过程分为两个阶段来执行。
准备阶段的主要目的是 确认所有事务参与者是否都准备好提交事务,具体步骤如下:
提交阶段的主要目的是根据 准备阶段的反馈结果,决定是真正提交事务还是回滚事务:
协调者提交请求:协调者向所有参与者发出正式提交事务的请求(即Commit请求)。
优点:
缺点:
XA 模式是从 1.2 版本支持的事务模式,Seata XA 模式是利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种事务模式。Seata 对 XA 模式进行了封装,使其更易于在微服务架构中使用。
主要特点:
DataSourceProxyXA 作为数据源代理,替代AT模式的 DataSourceProxy@GlobalTransactional 注解标记分布式事务入口
在 Seata 定义的分布式事务框架内,XA 模式利用事务资源(数据库等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务。
执行阶段(Execute):
报告状态:RM 将本地事务执行状态(成功或失败)报告给 TC。
完成阶段(Finish):
xa_commit 或 xa_rollback。Seata XA 模式的数据源代理设计对开发者友好,旨在透明化 XA 编程模型。它支持基于普通 DataSource 创建 XA 连接,无需开发者专门配置 XADataSource
数据在整个事务处理过程结束前,都被锁定,读写都按隔离级别的定义约束起来。
思考
数据锁定是获得更高隔离性和全局一致性所要付出的代价。
补偿型 的事务处理机制,在 执行阶段 即完成分支(本地)事务的提交,(资源层面)不锁定数据。而这是以牺牲 隔离性 为代价的。
另外,AT 模式使用 全局锁 保障基本的 写隔离,实际上也是锁定数据的,只不过锁在 TC 侧集中管理,解锁效率高且没有阻塞的问题。
XA prepare 后,分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞等待。
思考
协议的阻塞机制本身并不是问题,关键问题在于 协议阻塞 遇上 数据锁定。
如果一个参与全局事务的资源 “失联” 了(收不到分支事务结束的命令),那么它锁定的数据,将一直被锁定。进而,甚至可能因此产生死锁。
这是 XA 协议的核心痛点,也是 Seata 引入 XA 模式要重点解决的问题。
基本思路是两个方面:避免 “失联” 和 增加 “自解锁” 机制。
性能的损耗主要来自两个方面:一方面,事务协调过程,增加单个事务的 RT;另一方面,并发事务数据的锁冲突,降低吞吐。
思考
和不使用分布式事务支持的运行场景比较,性能肯定是下降的,这点毫无疑问。
本质上,事务(无论是本地事务还是分布式事务)机制就是拿部分 性能的牺牲 ,换来 编程模型的简单 。
与同为 业务无侵入 的 AT 模式比较:
首先,因为同样运行在 Seata 定义的分布式事务框架下,XA 模式并没有产生更多事务协调的通信开销。
其次,并发事务间,如果数据存在热点,产生锁冲突,这种情况,在 AT 模式(默认使用全局锁)下同样存在的。
所以,在影响性能的两个主要方面,XA 模式并不比 AT 模式有非常明显的劣势。
AT 模式性能优势主要在于:集中管理全局数据锁,锁的释放不需要 RM 参与,释放锁非常快;另外,全局提交的事务,完成阶段 异步化。
优点:
@GlobalTransactional 注解,无需深入修改业务代码。缺点:
接下来我们设计一套基于 Seata XA 模式的分布式事务实现方案。
环境准备
基于 Spring Cloud Alibaba 构建 cloud-seata-xa 项目,模块如下
代码实现可下载 cloud-seata-xa
提示
从编程模型上,XA 模式与 AT 模式保持完全一致,只需要修改数据源代理,即可实现 XA 模式与 AT 模式之间的切换。
Naocs 配置
在同分组下,根据服务中 seata.service.vgroup-mapping 定义的分组名称配置信息,内置的文本为集群名称 default。
yml seata: # 是否开启spring-boot自动装配 默认true enabled: true # 是否开启数据源自动代理 默认 true enable-auto-data-source-proxy: true # 是否使用JDK代理作为数据源自动代理的实现方式,默认false,采用CGLIB作为数据源自动代理的实现方式 use-jdk-proxy: false # 数据源代理模式 默认 AT data-source-proxy-mode: XA # Seata 应用ID,默认${spring.application.name} application-id: ${spring.application.name} # 事务分组 - 用于TC集群名 tx-service-group: seata-xa-group # 添加以下配置 service: vgroup-mapping: seata-xa-group: default # 你的事务分组对应的集群名称 disable-global-transaction: false

XA 协议通过两阶段提交(2PC) 机制,为分布式事务提供了强一致性的保证,是分布式事务领域经典且重要的解决方案。Seata 框架对 XA 进行了封装和优化,降低了开发复杂度,使得开发者能够通过简单的注解和配置,快速在微服务体系中引入强一致性分布式事务能力。
然而,XA 协议的同步阻塞和性能问题也是其固有的局限性。因此,在选择分布式事务方案时,需要根据实际业务场景对一致性和性能的要求进行权衡。对于追求高性能和最终一致性的场景,可以考虑 Seata 的 AT 模式或TCC 模式等柔性事务方案。
本文作者:张豪
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!