5.4.3 分布式事务标准规范: XA 规范
XA 规范 (eXtended Architecture) 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准。XA 规范使用 2PC 协议来保证所有资源同时提交或回滚任何特定的事务。
1. XA 规范概述
XA 规范是由 X/Open 组织定义的一套分布式事务处理的接口规范,旨在为应用程序和数据库管理系统(DBMS)提供一致的接口,以支持分布式事务处理。XA 规范定义了一组 API,应用程序可以使用这些 API 来管理分布式事务。
XA 规范主要涉及到以下三个角色:
- 应用程序(Application Program,AP):执行业务逻辑,并发起或参与分布式事务。
- 事务管理器(Transaction Manager, TM):负责管理分布式事务的整个生命周期,包括事务的提交、回滚和恢复等。
- 资源管理器(Resource Manager,RM):管理事务处理过程中涉及到的各种资源,如数据库、消息队列等。
在 XA 规范中,应用程序使用 XA API 与事务管理器进行交互,而事务管理器使用 XA API 与各个资源管理器进行交互。XA 规范定义了一组标准的接口函数,包括开始全局事务、结束全局事务、提交全局事务、回滚全局事务等。通过这些接口函数,应用程序可以实现分布式事务的提交和回滚,从而保证事务的一致性和可靠性。
总的来说,XA 规范为应用程序和数据库提供了一套通用的接口,使得分布式事务处理变得更加容易和可靠。各大数据库厂商都实现了 XA 规范,因此应用程序可以在不同的数据库系统之间进行无缝的移植。
2. 支持 XA 规范的数据库
很多关系型数据库都支持 XA 协议。以下是一些主流数据库的支持情况:
- MySQL: 支持 XA 事务,并实现了 XA 接口。
- PostgreSQL: 从版本 8.0 开始支持 XA 事务,通过插件接口实现。
- Oracle: 支持 XA 事务,使用 OracleXADataSource 提供 XA 接口。
- SQL Server: 支持 XA 事务,使用 MSDTC 提供 XA 接口。
- DB2: 支持 XA 事务,使用 DB2 Universal JDBC 驱动器提供 XA 接口。
- Sybase ASE: 支持 XA 事务,使用 JConnect 提供 XA 接口。
5.4.4 应用层面的分布式事务协议: TCC
TCC(Try、Confirm、Cancel)是一种基于补偿事务的分布式事务协议,TCC 和 2PC 有很大的相似性,不过两者面向的场景不同,2PC 是在数据库层面实现的分布式事务协议,TCC 则是应用层面实现的分布式事务协议。
TCC 核心思想是:“针对每个操作都要注册一个与其对应的确认(Try)和补偿(Cancel)”, 整个事务流程通常由三个阶段组成:
- Try 阶段:在这个阶段中,参与者会尝试执行事务,并将所有要修改的数据保存在一个本地事务中。如果所有的参与者都执行成功,那么它们就会向协调者报告这一事实。
- Confirm 阶段:如果所有的参与者都成功执行了 Try 阶段,那么协调者会向所有参与者发出确认提交的指令。在这个阶段中,参与者将提交事务,释放资源,并向协调者报告操作成功。
- Cancel 阶段:如果在 Try 阶段中有任何参与者执行失败,那么协调者会向所有参与者发出取消事务的指令。在这个阶段中,参与者将回滚事务,释放资源,并向协调者报告操作失败。
通过这些步骤,TCC 协议可以保证在分布式系统中的事务操作要么全部提交,要么全部回滚,从而确保了数据的一致性。
TCC 示例
以一个下单服务为例,说明 TCC 事务处理流。该下单服务由两个系统操作完成:订单系统 X、资金账户系统 Y。
- Try 操作 : try X 下单系统创建待支付订单。try Y 自己账户系统冻结订单金额 100 元。
- Confirm 操作 confirm X 订单更新为支付成功。confirm Y 扣减账户系统 100 元。
- Cancel 操作 Cancel X 订单异常,资金退回,Cancel Y 扣款异常,订单支付失败
订单逻辑代码示例:
/** TCC 业务三个阶段实现 */ public class PayService { // Try 事务准备阶段 int prepareOrder() { // 检测账户余额 Account account = accountService.get(uid = 10000); if (account.getAmount() < 100) throw new Exception("账户余额不足") // 冻结支付金额 account.setFreezedAmount(200); } // 如果一阶段的工作都顺利进行完了,则进行二阶段的事务提交 Boolean confirm() { Account account = accountService.get(uid = 10000); // 释放冻结的金额 account.setFreezedAmount(100); // 进行扣款操作 account.setAmount(100); return True; } // 如果一阶段的方法执行出问题了,二阶段就要回滚,进行各类反向补偿操作 Boolen cancel() { Account account = accountService.get(uid = 10000); // 取消冻结金额 account.setFreezedAmount(account.getFreezedAmount() - 100); } } public class OrderService { // 下单 func order() { try { // 第一阶段 Try 操作 PayService.prepareOrder() } catch { // 回滚补偿操作 PayService.cancel() throw new Exception("error") } // 第一阶段正常,进行 confirm 操作 PayService.confirm() } }
通过上面的逻辑代码可以看出,感知各个阶段的执行情况以及推进执行下一个阶段的这些事情,需要编写大量的代码。通常的情况,实际环境中,一般引入一款 TCC 分布式事务框架(Seata、ByteTCC、TCC-transaction)提升开发效率。
小结
TCC 事务相当于 2PC 的一种升级,相对于 2PC, TCC 不再依赖于本地数据库事物能力,并把 2PC 的提交跟回滚操作明确的抽象成 Confirm 跟 Cancel ,TCC 可以专注解决应用层面的事务。TCC 事务在逻辑上是比较清晰,但是代码开发量很大(每个子系统都要实现三个接口),对业务有很强的侵入性,另外因为 TCC 有重试机制,所有的接口都需要实现幂等。
0 评论