分布式事务一致性(2)
MySQL5.7.7版本之后,对XA的支持比较好。但官方文档对如何通过MySQL客户端体验XA写的比较简单。
mysql> XA START 'xatest';
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO mytable (i) VALUES(10);
Query OK, 1 row affected (0.04 sec)
mysql> XA END 'xatest';
Query OK, 0 rows affected (0.00 sec)
mysql> XA PREPARE 'xatest';
Query OK, 0 rows affected (0.00 sec)
mysql> XA COMMIT 'xatest';
Query OK, 0 rows affected (0.00 sec)
这篇博客spring整合atomikos实现分布式事务内含GitHub源码。里面提到的是atomikos同时管理MySQL和Oracle数据库里的一张表,实现将一笔金额从MySQL表里转出,同时转入Oracle表里,这是一个最简单的分布式事务使用场景。现在Oracle环境很难搞了,我在本机使用两个不同IP的MySQL库替代。摘录出如下成功完成转账业务日志,行号是我手工添加的,可以暂时忽略。
行号35 19-10-11 09:33:41,927 [http-80-2] XAResource.start ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D31 , XAResource.TMNOFLAGS ) on resource dataSource1 represented by XAResource instance [email protected]
行号51 19-10-11 09:33:42,384 [http-80-2] XAResource.start ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D32 , XAResource.TMNOFLAGS ) on resource dataSource2 represented by XAResource instance com.mysq[email protected]
行号55 19-10-11 09:33:42,394 [http-80-2] XAResource.end ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D31 , XAResource.TMSUCCESS ) on resource dataSource1 represented by XAResource instance [email protected]
行号57 19-10-11 09:33:42,395 [http-80-2] XAResource.end ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D32 , XAResource.TMSUCCESS ) on resource dataSource2 represented by XAResource instance [email protected]
行号59 19-10-11 09:33:42,404 [http-80-2] XAResource.prepare ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D31 ) returning OK on resource dataSource1 represented by XAResource instance [email protected]
行号60 19-10-11 09:33:42,409 [http-80-2] XAResource.prepare ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D32 ) returning OK on resource dataSource2 represented by XAResource instance [email protected]
行号61 19-10-11 09:33:42,419 [http-80-2] XAResource.commit ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D31 , false ) on resource dataSource1 represented by XAResource instance [email protected]
行号62 19-10-11 09:33:42,421 [http-80-2] XAResource.commit ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D32 , false ) on resource dataSource2 represented by XAResource instance [email protected]
猜测一下上述日志摘录的意思:
- atomikos在交叉管理两个分支事务,并成功提交:start、一组增删改查操作(上述日志暂时未体现)、end、prepare、commit。
- 括号里冒号前的长串数字表示全局事务ID,冒号后面的内长串数字表示分支事务ID。两个长串数字合在一起有一个专业名称:XID。
摘录抛出运行期异常的日志摘录:
行号35 19-10-12 09:59:02,362 [http-80-3] XAResource.start ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D31 , XAResource.TMNOFLAGS ) on resource dataSource1 represented by XAResource instance [email protected]
行号51 19-10-12 09:59:02,834 [http-80-3] XAResource.start ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D32 , XAResource.TMNOFLAGS ) on resource dataSource2 represented by XAResource instance [email protected]
行号55 19-10-12 09:59:02,845 [http-80-3] XAResource.end ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D31 , XAResource.TMSUCCESS ) on resource dataSource1 represented by XAResource instance [email protected]
行号57 19-10-12 09:59:02,845 [http-80-3] XAResource.end ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D32 , XAResource.TMSUCCESS ) on resource dataSource2 represented by XAResource instance [email protected]
行号58 19-10-12 09:59:02,852 [http-80-3] XAResource.rollback ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D31 ) on resource dataSource1 represented by XAResource instance [email protected]
行号59 19-10-12 09:59:02,855 [http-80-3] XAResource.rollback ( 636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D30303030313030303031:636F6D2E61746F6D696B6F732E737072696E672E6A6462632E746D32 ) on resource dataSource2 represented by XAResource instance [email protected]
- atomikos在交叉管理两个分支事务,最终全部回滚:start、一组增删改查操作(上述日志暂时未体现)、end、rollback。
关键Java接口XAResource介绍
javax.transaction.xa.XAResource接口是对《Distributed Transaction Processing The XA Specification.pdf》(以下简称XA规范)的Java映射。
在XA规范中,定义了一系列“资源管理器”和“事务管理器”之间的协定,XAResource接口就是对这些协定的Java抽象。MySQL驱动程序里,com.mysql.jdbc.jdbc2.optional.MysqlXAConnection类就实现了该接口。“资源管理器”和“事务管理器”之所以加引号,是因为它们是XA规范里的专业术语,摘录如下:
2.2.4 Resource Manager
An RM manages a certain part of the computer’s shared resources. Many other software entities can request access to the resource from time to time, using services that the RM provides. Here are some examples of RMs:
• A database management system (DBMS) is an RM. Typical DBMSs are capable of defining transactions and committing work atomically.
• A file access method such as the Indexed Sequential Access Method (ISAM) can be the basis for an RM. Typically, an ISAM RM must be enhanced to support transactions as defined herein.
• A print server might be implemented as an RM.
A single RM may service multiple independent resource domains. An RM instance services one of these domains. (See also Section 3.2 on page 13.) Unless specified otherwise, operations this specification allows on an RM are allowed on each RM instance.
通俗地讲,我们上述例子中就有两个RM,即两个独立IP的MySQL数据库。
2.2.7 Transaction Manager
TMs manage global transactions, coordinate the decision to commit them or roll them back, and coordinate failure recovery. The AP defines the start and end of a global transaction by calling a TM. The TM assigns an identifier to the global transaction (see Section 4.2 on page 19). The TM manages global transactions and informs each RM of the XID on behalf of which the RM is doing work. Although RMs can manage their own recoverable work units as they see fit, each RM must accept XIDs and associate them with those work units. In this way, an RM knows what recoverable work units to complete when the TM completes a global transaction.
通俗地讲,TM是一个协调人,他指挥所有参与者,即所有RM,完成一个全局事务:要么全部提交,要么全部回滚,要么全部失败恢复。
再来一次操作,在命令行操作:
mysql> XA start 'A000000001', 'B00001';
Query OK, 0 rows affected (0.00 sec)
// 一些增删改查操作,此处略
mysql> XA end 'A000000001', 'B00001';
Query OK, 0 rows affected (0.00 sec)
mysql> XA prepare 'A000000001', 'B00001';
Query OK, 0 rows affected (0.00 sec)
mysql> XA RECOVER;
+----------+--------------+--------------+--------------------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+--------------------+
| 1 | 10 | 8 | A000000001B0000452 |
| 1 | 10 | 6 | A000000001B00001 |
+----------+--------------+--------------+--------------------+
2 rows in set (0.00 sec)
在MySQL GUI客户端操作:
XA start 'A000000001', 'B0000452';
// 一些增删改查操作,此处略
XA end 'A000000001', 'B0000452';
XA prepare 'A000000001', 'B0000452';
XA RECOVER;
最后一行执行后看到了和命令行一样的结果。
通俗地解释一下:
- 在命令行客户端新建一个全局事务,其ID是A000000001,新建一个分支事务,其ID是B00001。
- 在GUI客户端新建一个全局事务,其ID是A000000001。其实,和步骤1里的全局事务是同一个。新建一个分支事务,其ID是B0000452。
- 执行XA RECOVER后看到的表格:有一个全局事务,内有两个分支事务,全局事务ID(gtrid_length)长度为10,即A000000001的长度,两个分支事务ID的长度(bqual_length)分别为8和6,即B0000452和B00001的长度。全局事务ID拼接分支事务ID就是data列的值。