在数据库中,XA事务全称是 “eXtended Architecture Transaction”,即扩展架构事务。MySQL5.0.3版本开始支持 XA 分布式事务,并且只有InnoDB存储引擎支持XA事务。全局事务涉及多个本身具有事务性的操作,但所有操作都必须作为一个组成功完成,或者作为一个组合回滚。本质上,这将ACID属性“向上扩展”,以便多个ACID事务可以作为具有ACID属性的全局操作的组件协同执行。
1.使用全局事务的应用程序涉及一个或多个资源管理器和一个事务管理器:
- 资源管理器(RM)提供对事务性资源的访问。数据库服务器是一种资源管理器。必须能够提交或回滚RM管理的事务。
- 事务管理器(TM)协调作为全局事务一部分的事务。它与处理这些事务的RM进行通信。全局事务中的单个事务是全局事务的“分支”。全局事务及其分支由稍后描述的命名方案标识。
2.执行全局事务的过程使用两阶段提交(2PC,= PrePare+Commit ):
- 第一阶段,所有分支机构都已准备就绪。也就是说,TM告诉他们准备做出承诺。通常,这意味着管理分支的每个RM都会在稳定存储中记录分支的操作。分支指示它们是否能够做到这一点,这些结果用于第二阶段。
- 第二阶段,TM告诉RM是提交还是回滚。如果所有分支在准备就绪时都表示能够提交,则所有分支都会被告知提交。如果任何分支在准备时表示无法提交,则所有分支都会被告知回滚。
备注:在某些情况下,全局事务可能会使用单阶段提交(1PC)。例如,当事务管理器发现全局事务仅由一个事务资源(即单个分支)组成时,可以告诉该资源同时准备和提交。
3.MySQL中实现XA事务,使用MySQL服务够充当资源管理器,在全局事务中处理XA事务。连接到MySQL服务器的客户端程序充当事务管理器。
XA事务介绍
在MySQL中,xid是XA事务标识符。xid值通常由事务管理器生成。一个TM生成的值必须与其他TM生成的不同。给定的TM必须能够在XA RECOVER语句返回的值列表中识别自己的xid值。它指示语句应用于哪个事务。xid值由客户端提供,或由MySQL服务器生成。xid值由一到三部分组成:
xid: gtrid [, bqual [, formatID ]]
在MySQL官方文档中,关于xid的组成也有类似的说明:
gtrid : 是一个全局事务标识符(global transaction identifier),
bqual:是一个分支限定符(branch qualifier),如果没有提供bqual,那么默认值为空字符串’’。
formatID:是一个数字,用于标记gtrid和bqual值的格式,这是一个无符号整数(unsigned integer),也就是说,最小为0。如果没有提供formatID,那么其默认值为1。
备注:特别需要注意的是,xid作为一个事务分支的标识符,理论上只要有分支限定符(bqual)就可以了,为什么要包含全局事务标识符(gtrid)?这主要是为了管理方便,通过包含进xid,可以很容易的判断出这个事务分支属于哪一个全局事务。
在MySQL XA事务中 有6种状态和语句支撑:
XA {START|BEGIN} xid [JOIN|RESUME] #开始一个XA事务,设置事务的XID。一般使用XA START。
XA END xid [SUSPEND [FOR MIGRATE]] #结束当前的XA事务段,但不是提交事务,需要配合PREPARE使用。
XA PREPARE xid #准备提交XA事务。
XA COMMIT xid [ONE PHASE] #提交XA事务。
XA ROLLBACK xid #事务XA回滚。
XA RECOVER [CONVERT XID] #列出所有处于PREPARE阶段的XA事务。
执行过程如图:
当然这些XA事务,也会如实写到binlog日志中。通过mysqlbinlog工具解析binlog内容,可以看到对应的XA事务相关记录内容。对于整个过程记录的非常详细。
shell# mysqlbinlog --no-defaults --base64-output=decode-rows -v -v mysql-bin.000010
XA START X'786174657374',X'',1
/*!*/;
# at 594
#240718 16:23:21 server id 129 end_log_pos 652 CRC32 0x96168f23 Rows_query
# INSERT INTO mytable (id) VALUES(2)
# at 652
#240718 16:23:21 server id 129 end_log_pos 705 CRC32 0xb76e0470 Table_map: `demo`.`mytable` mapped to number 89
# has_generated_invisible_primary_key=0
# at 705
#240718 16:23:21 server id 129 end_log_pos 745 CRC32 0xf5314a82 Write_rows: table id 89 flags: STMT_END_F
### INSERT INTO `demo`.`mytable`
### SET
### @1=2 /* INT meta=0 nullable=0 is_null=0 */
# at 745
#240718 16:23:28 server id 129 end_log_pos 843 CRC32 0xa334d3a1 Query thread_id=11 exec_time=0 error_code=0
SET TIMESTAMP=1721291008/*!*/;
XA END X'786174657374',X'',1
/*!*/;
# at 843
#240718 16:23:28 server id 129 end_log_pos 885 CRC32 0x57d95025 XA PREPARE X'786174657374',X'',1
XA PREPARE X'786174657374',X'',1
/*!*/;
ROLLBACK /* added by mysqlbinlog */ /*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
XA事务常见问题
在MySQL中,很少使用XA事务,但使用过XA事务,应该都碰到过如下问题。当然还有主从复制XA事务丢失问题也会存在。下面是碰到的XA事务常见两个问题。
1.XA事务无法释放
在MySQL中XA事务也有致命的缺陷,就是没有TimeOut机制。如,XA事务PREPARE阶段中Crash时,事务还保留着,不会滚也不会提交。如果不去处理,XA事务一直保留,当用到相关表数据,就会产生锁等待。这种情况下需要程序提交/回滚 或人为干涉。
下面实例,当到PREPARE阶段,重新启动MySQL服务,XA事务还存在情况:
2.XA事务无法识别
在MySQL中,使用XA事务,难免XID中包含乱码特殊符号之类的。当COMMIT/ROLLBACK无法识别XID怎么处理。这时就可以使用XA RECOVER CONVERT XID命令行。实例如下:
Session1:构建XA事务
mysql> XA START 'XA-1234-B/$%^C 5D';
mysql> INSERT INTO mytable (id) VALUES(3);
mysql> XA END 'XA-1234-B/$%^C 5D';
mysql> XA PREPARE 'XA-1234-B/$%^C 5D';
Session2:回滚XA事务
mysql> XA RECOVER;
+----------+--------------+--------------+----------------------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+----------------------+
| 1 | 20 | 0 | XA-1234-B/$%^C 5D♣ |
+----------+--------------+--------------+----------------------+
1 row in set (0.00 sec)
#使用16进制查看
mysql> XA RECOVER CONVERT XID;
+----------+--------------+--------------+--------------------------------------------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+--------------------------------------------+
| 1 | 20 | 0 | 0x58412D313233342D422F24255E43203544E299A3 |
+----------+--------------+--------------+--------------------------------------------+
1 row in set (0.00 sec)
字段说明:
- formatID:是事务xid的formatID部分。
- gtrid_length:是xid的gtrid部分的字节长度。
- bqual_length:是xid的bqual部分的字节长度。
- data:是xid的gtrid和bqual部分的连接。
数据分布说明:
- 这里的’0x58412D313233342D422F24255E43203544E299A3’即是’XA-1234-B/$%^C 5D♣’的十六进制编码。
- bqual_length 0就是空。
- format格式化值是1
通过xid结构(xid: gtrid [, bqual [, formatID ]])和 上面binlog内容XA的提交方式(XA START X’58412d353637382d453d2f24255e46203547e29885’,X’’,1)就知道怎样编写语句。
回滚语句如下:
语句:XA ROLLBACK X’去掉0x的后面数值’,X’bqua值’ , 格式化formatID值
mysql> XA ROLLBACK X'58412D313233342D422F24255E43203544E299A3', X'' , 1;
Query OK, 0 rows affected (0.00 sec)
XA相关参数
1.xa_detach_on_prepare
从MySQL 8.0.29及更高版本支持分离的XA事务。执行XA PREPARE后,分离的事务将与当前会话断开连接(并且可以由另一个连接提交或回滚)。这意味着当前会话可以自由启动新的本地事务或XA事务,而无需等待准备好的XA事务被提交或回滚。
当OFF时,其他Session无法提交/回滚XA事务:
当ON时,,其他Session能提交/回滚XA事务:
2.innodb_support_xa
MySQL5.7支持的XA事务参数。启用InnoDB对两阶段提交的支持,从而为事务准备带来额外的磁盘刷新。如果禁用该参数,事务可以以与实时数据库提交它们不同的顺序写入binlog,会导致恢复或副本上重放binlog时产生不同的数据。
从MySQL8.0版本已经被弃用,同时跟innodb_support_xa相关的所有代码都被删除。
总结
按照经验在MySQL中尽量避免使用XA事务。如使用XA事务,需要特别注意以下点:
- 在MySQL中,仅InnoDB存储引擎支持XA事务,MyISAM等其他引擎不支持。
- 如果在事务进行中会话被中断(如网络问题、客户端崩溃等),可能需要进行特殊处理。
- 在高并发场景下,XA事务可能导致性能瓶颈。