XA,2PC,two-phase commit protocol,两阶段事务提交采⽤的是 X/OPEN 组织定义的DTP 模型所抽象的:
- AP
应用程序,Application Program,定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作 - TM(事务管理器)
Transaction Manager,负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等 - RM(资源管理器)
Resource Manager,如数据库、文件系统等,并提供访问资源的方式
XA 接口
xa_start :负责开启或者恢复一个事务分支
xa_end: 负责取消当前线程与事务分支的关联
xa_prepare:询问 RM 是否准备好提交事务分支
xa_commit:通知 RM 提交事务分支
xa_rollback: 通知 RM 回滚事务分支
xa_recover : 需要恢复的 XA 事务
保证分布式事务的强⼀致性。其中 TM 与 RM 间采⽤ XA 协议进⾏双向通信。
XA 整体设计思路可概括为:在现有事务模型基础上微调扩展而实现的分布式事务。
与传统的本地事务相⽐,XA 事务增加了准备阶段,数据库除了被动接受提交指令外,还可以反向通知调⽤⽅事务是否可以被提交。TM 可以收集所有分⽀事务的准备结果,并于最后进⾏原⼦提交,以保证事务的强⼀致性。
- 两阶段提交模型
- Java 通过定义 JTA 接口实现了 XA 模型
JTA 接口中的 ResourceManager 需要数据库⼚商提供 XA 驱动实现,TransactionManager 则需要事务管理器的⼚商实现,传统的事务管理器需要同应⽤服务器绑定,因此使⽤的成本很⾼。而嵌⼊式的事务管器可以以 jar 包的形式提供服务,同 Apache ShardingSphere集成后,可保证分⽚后跨库事务强⼀致性。通常,只有使⽤了事务管理器⼚商所提供的 XA 事务连接池,才能⽀持 XA 的事务。Apache ShardingSphere在整合 XA 事务时,采⽤分离 XA 事务管理和连接池管理的⽅式,做到对应⽤程序的零侵⼊。
MySQL 从5.0.3开始支持 InnoDB 引擎的 XA 分布式事务,MySQL Connector/J 从5.0.0版本开始支持 XA。
在 DTP 模型中,MySQL 属于资源管理器(RM)。分布式事务中存在多个 RM,由事务管理器 TM 来统一进行协调。
MySQL 的 XA
- XA {START | BEGIN} xid [JOIN | RESUME]
开启XA事务,如果使用的是XA START而非XA BEGIN,那么不支持[JOIN | RESUME],xid是个唯一值,表示事务分支标
全局 + 分支 id
- XA END xid [SUSPEND [FOR MIGRATE]]
结束一个xA事务,不支持[SUSPEND [FOR MIGRATE]] - XA PREPARE xid
准备提交
- XA COMIT xid [ONE PHASE]
提交,如果使用了 ONE PHASE,贼表示使用一阶段提交。两阶段提交协议中,如果只有一个 RM 参与,那么可以优化为一阶段提交 - XA ROLLBACK xid
回滚
- XA recover[convert xid]
列出所有处于 prepare 阶段的 XA 事务
mysql> select * from t; +----+------+------+ | id | c | d | +----+------+------+ | 0 | 0 | 0 | | 5 | 5 | 5 | | 10 | 10 | 10 | | 15 | 15 | 15 | | 20 | 20 | 20 | | 25 | 25 | 25 | | 30 | 10 | 30 | +----+------+------+ 7 rows in set (0.01 sec) mysql> xa start 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> insert into t values(1,2,3); Query OK, 1 row affected (0.01 sec) mysql> update t set id = 11 where id = 10; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> xa end 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> xa prepare 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> xa commit 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> select * from t; +----+------+------+ | id | c | d | +----+------+------+ | 0 | 0 | 0 | | 1 | 2 | 3 | # 添加的记录 | 5 | 5 | 5 | | 11 | 10 | 10 | # 修改的记录 | 15 | 15 | 15 | | 20 | 20 | 20 | | 25 | 25 | 25 | | 30 | 10 | 30 | +----+------+------+ 8 rows in set (0.01 sec) mysql> xa start 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> insert into t values(2,5,8); Query OK, 1 row affected (0.00 sec) mysql> xa end 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> xa prepare 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> xa recover; # gtr 全局事务 id # bq 分支事务 id,这就能区分是大事务,还是小事务 +----------+--------------+--------------+------+ | formatID | gtrid_length | bqual_length | data | +----------+--------------+--------------+------+ | 1 | 3 | 0 | x01 | +----------+--------------+--------------+------+ 1 row in set (0.00 sec) mysql> xa start 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> insert into t values(2,5,8); Query OK, 1 row affected (0.00 sec) mysql> xa end 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> xa prepare 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> xa recover; +----------+--------------+--------------+------+ | formatID | gtrid_length | bqual_length | data | +----------+--------------+--------------+------+ | 1 | 3 | 0 | x01 | +----------+--------------+--------------+------+ 1 row in set (0.00 sec) mysql> xa rollback 'x01'; Query OK, 0 rows affected (0.00 sec) mysql> select * from t; +----+------+------+ | id | c | d | +----+------+------+ | 0 | 0 | 0 | | 1 | 2 | 3 | | 5 | 5 | 5 | | 11 | 10 | 10 | | 15 | 15 | 15 | | 20 | 20 | 20 | | 25 | 25 | 25 | | 30 | 10 | 30 | +----+------+------+ 8 rows in set (0.00 sec)