mysql 内部xa_MySQL-XA事务(一)简介

8df8e23c9ae308a172c8dfb5ff3dde57.pngcf7c48fd91afc98ab88f7087c3deb592.png01-分布式事务处理机制DTP模型e8d6b3254373121cf1852ab9d4f88752.png

MySQL XA事务是基于Open Group 的<> 标准来实现的,目的是为了适配分布式事务,允许多个数据库实例参与到一个全局的事务中。XA事务功能在很早之前就被引入了,直到MySQL-5.7版本才趋于完善,很多数据库中间件也开始使用XA事务来实现分布式事务。先来看下Open Group组织所定义的分布式事务处理模型,也被称为DTP模型,如下:

ffdf4277224dcb5fce7ba605e68a420a.png

其中涉及到的AP/RM/TM三个角色和定义如下:AP(Application Program):应用程序,主要是定义事务边界以及那些组成事务的特定于应用程序的操作。

RM(Resouces Manager):资源管理器,管理一些共享资源的自治域,如提供对诸如数据库之类的共享资源的访问。

TM(Transaction Manager):事务管理器,管理全局事务,协调事务的提交或者回滚,并协调故障恢复。ec1ce9949fddbec3be2ce631e7f84c5f.pngcf7c48fd91afc98ab88f7087c3deb592.png02-MySQL-XA事务操作e8d6b3254373121cf1852ab9d4f88752.png

在理解MySQL XA事务的工作原理时,需要将X/Open DTP模型中的各个组件映射到相关的MySQL组件中。MySQL引入binlog存储引擎作为操作日志的存储和传输,binlog和innobase同时参与到事务的提交过程,无论是普通事务操作还是XA事务操作,在MySQL内部都是一个分布式事务的处理过程,不同点在于MySQL XA事务的提交过程中将commit(2pc)操作显示的分为XA prepare 和XA commit两个过程来处理(细节稍有不同),以此来适配全局分布式事务。DTP模型与MySQL内部XA事务的对应关系如下:

356e496d2e84e83083d22db679981143.png

而在全局分布式事务中,MySQL实例对应到DTP中的RM,如下:

e4865598c415199b32a99b2bdf551b27.png

MySQL对外提供了如下6条命令,供事务管理器来进行调度。xa start 开启一个XA事务

xa end 结束一个XA事务操作

xa prepare 准备提交一个XA事务

xa commit 正式提交一个XA事务

xa rollback 回滚一个XA事务

xa recover 列出所有处于prepared状态的XA事务

实际操作一遍就能明白,如下://开启XA事务

mysql> xa start 'trx1';

Query OK, 0 rows affected (0.00 sec)

//事务操作

mysql> insert into t1(name) values('ashe');

Query OK, 1 row affected (0.00 sec)

//事务操作

mysql> insert into t1(name) values('zed');

Query OK, 1 row affected (0.00 sec)

//结束XA事务操作

mysql> xa end 'trx1';

Query OK, 0 rows affected (0.00 sec)

//预提交XA事务

mysql> xa prepare 'trx1';

Query OK, 0 rows affected (0.00 sec)

//正式提交XA事务

mysql> xa commit 'trx1';

Query OK, 0 rows affected (0.00 sec)查看对应的binlog文件,可以发现,MySQL-5.7版本中引入了XA_prepare_log_event,将binlog日志一分为二,整体占用两个GTID,所以如果是在半同步复制场景下,一个XA事务的提交过程,需要两次ACK的等待(commit one phase除外),如下:SET @@SESSION.GTID_NEXT= '5a28b508-aa9d-11e9-99e8-8f54f8e077b5:4'/*!*/;

# at 299

#190807 21:11:30 server id 110110 end_log_pos 392 CRC32 0x22e49420 Query thread_id=3 exec_time=0 error_code=0

SET TIMESTAMP=1565183490/*!*/;

SET @@session.pseudo_thread_id=3/*!*/;

SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;

SET @@session.sql_mode=1073741824/*!*/;

SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;

/*!\C utf8 *//*!*/;

SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=45/*!*/;

SET @@session.lc_time_names=0/*!*/;

SET @@session.collation_database=DEFAULT/*!*/;

XA START X'74727831',X'',1

/*!*/;

# at 392

#190807 21:11:30 server id 110110 end_log_pos 451 CRC32 0xf18efdf5 Rows_query

# insert into t1(name) values('ashe')

# at 451

#190807 21:11:30 server id 110110 end_log_pos 499 CRC32 0xae2d7b3e Table_map: `ashe`.`t1` mapped to number 108

# at 499

#190807 21:11:30 server id 110110 end_log_pos 544 CRC32 0x05941ec3 Write_rows: table id 108 flags: STMT_END_F

### INSERT INTO `ashe`.`t1`

### SET

### @1=2 * INT meta=0 nullable=0 is_null=0 */

### @2='ashe' * VARSTRING(40) meta=40 nullable=1 is_null=0 */

# at 544

#190807 21:11:34 server id 110110 end_log_pos 602 CRC32 0x7e7c4788 Rows_query

# insert into t1(name) values('zed')

# at 602

#190807 21:11:34 server id 110110 end_log_pos 650 CRC32 0xdd1eb844 Table_map: `ashe`.`t1` mapped to number 108

# at 650

#190807 21:11:34 server id 110110 end_log_pos 694 CRC32 0x6d7e2039 Write_rows: table id 108 flags: STMT_END_F

### INSERT INTO `ashe`.`t1`

### SET

### @1=3 * INT meta=0 nullable=0 is_null=0 */

### @2='zed' * VARSTRING(40) meta=40 nullable=1 is_null=0 */

# at 694

#190807 21:11:56 server id 110110 end_log_pos 785 CRC32 0xb00e4eb4 Query thread_id=3 exec_time=0 error_code=0

SET TIMESTAMP=1565183516/*!*/;

XA END X'74727831',X'',1

/*!*/;

# at 785

#190807 21:11:56 server id 110110 end_log_pos 825 CRC32 0x239f7336 XA PREPARE X'74727831',X'',1

XA PREPARE X'74727831',X'',1

/*!*/;

# at 825

#190807 21:12:00 server id 110110 end_log_pos 890 CRC32 0x73388f78 GTID last_committed=1 sequence_number=2 rbr_only=no

SET @@SESSION.GTID_NEXT= '5a28b508-aa9d-11e9-99e8-8f54f8e077b5:5'/*!*/;

# at 890

#190807 21:12:00 server id 110110 end_log_pos 984 CRC32 0x04f221b5 Query thread_id=3 exec_time=0 error_code=0

SET TIMESTAMP=1565183520/*!*/;

XA COMMIT X'74727831',X'',1

/*!*/;

ROLLBACK /* added by mysqlbinlog */ /*!*/;

SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;

DELIMITER ;

# End of log file

/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;

/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

ec1ce9949fddbec3be2ce631e7f84c5f.pngcf7c48fd91afc98ab88f7087c3deb592.png03-MySQL-XA事务状态流转e8d6b3254373121cf1852ab9d4f88752.png

XA事务在MySQL内部共有五种状态,如下:XA_NOTR     //未开始

XA_ACTIVE 活跃状态

XA_IDLE  //空闲状态

XA_PREPARED //准备提交状态

XA_ROLLBACK_ONLY /只能回滚

状态流转图:

36001b9c94859ee8cd604d463c606fec.png

a6af313a2dc707e47400479c34b053b2.png

事务状态流转受到InnoDB参数innodb_rollback_on_timeout的影响,这一点会在下一篇文章中解释,关于MySQL XA事务处理的内部细节,也会放在后续的文章中。a7fcab0a8cc87e7c129f122ec44a253b.png