实践分支事务 id
mysql> xa start 'x01', 'b01'; 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) mysql> insert into t values(2,5,8); Query OK, 1 row affected (0.00 sec) mysql> select * from t; +----+------+------+ | id | c | d | +----+------+------+ | 0 | 0 | 0 | | 1 | 2 | 3 | | 2 | 5 | 8 | | 5 | 5 | 5 | | 11 | 10 | 10 | | 15 | 15 | 15 | | 20 | 20 | 20 | | 25 | 25 | 25 | | 30 | 10 | 30 | +----+------+------+ 9 rows in set (0.00 sec) mysql> xa end 'x01', 'b01'; Query OK, 0 rows affected (0.00 sec) mysql> xa prepare 'x01', 'b01'; Query OK, 0 rows affected (0.01 sec) mysql> xa recover; +----------+--------------+--------------+--------+ | formatID | gtrid_length | bqual_length | data | +----------+--------------+--------------+--------+ | 1 | 3 | 3 | x01b01 | +----------+--------------+--------------+--------+ 1 row in set (0.01 sec) # gtrid_length 全局事务 id 的长度 # bqual_length 分支事务 id 的长度 mysql> xa commit 'x01', 'b01'; Query OK, 0 rows affected (0.00 sec) mysql> xa recover; Empty set (0.00 sec) mysql> select * from t; +----+------+------+ | id | c | d | +----+------+------+ | 0 | 0 | 0 | | 1 | 2 | 3 | | 2 | 5 | 8 | | 5 | 5 | 5 | | 11 | 10 | 10 | | 15 | 15 | 15 | | 20 | 20 | 20 | | 25 | 25 | 25 | | 30 | 10 | 30 | +----+------+------+ 9 rows in set (0.00 sec)
- MySQL XA事务状态
ACTIVE状态的XA事务,我们可以执行构成事务的SQL语句,然后发布一个XA END语句。XA END把事务放入IDLE状态。
XA事务和非XA事务(即本地事务)是互斥的。
例如,已经执行了 XA START 开启一个XA事务,则本地事务不会被启动,直到XA事务已被提交或被回滚为止。相反的,若已使用START TRANSACTION启动一个本地事务,则XA语句不能被使用,直到该事务被提交或被回滚为止。
时序图
完整的 XA 事务处理时序图
- 单个 MySQL 的内部操作
XA 过程中,事务失败怎么办?
业务 SQL 执行过程,某个 RM 崩溃怎么处理,全部 prepare 后,某个 RM 崩溃怎么处理,commit 时,某个 RM 崩溃怎么办?
MySQL < 5.7的bug
- 已经prepare的事务, 在客户端退出或者服务宕机的时候,2PC的事务 会被回滚。
- 在服务器故障重启提交后, 相应的Binlog被丢失
MySQL 5.6版本在客户端退出的时候,自动把已经prepare的事务回滚了 ,那么MySQL为什么要这样做?这主要取决于MySQL的内部实现,MySQL 5.7以前的版本,对于prepare的事务,MySQL 是不会记录binlog的(官方说是减少fsync, 起到了优化的作用)。只有当分布式事务提交的时候才会把前面的操作写入binlog信息,所以对于binlog来说,分布式事务与普通的事务没有区别,而prepare以
前的操作信息都保存在连接的I0 CACHE中,如果这个时候客户端退出了,以前的binlog信息都会被丢失,再次重连后允许提交的话,
会造成Binlog丢失,从而造成主从数据的不一致,所以官方在客户端退出的时候直接把已经prepare的事务都回滚了!
MySQL > 5.7版本的优化
https://dev.mysql.com/worklog/task/?id=6860,MySQL对于分布式事务,在prepare的时候就完成了写Binlog的操作,通过新增一种叫XA _prepare_ log_ event的event类型来实现,这与以前版本的主要区别(以前版本prepare时不写Binlog)。
事务管理器,负责协调多个数据库(资源管理器)的事务。
- 事务管理器先问各个DB:预提交 ok 吗?
- 如果每个数据库都回复ok,即预提交成功,开始正式提交事务,在各DB开始执行操作,这里失败会有失败异常重试,日志分析,人工重试。
X/Open,即现在的open group,是一个独立的组织,主要负责制定各种行
业技术标准。X/Open组织主要由各大知名公司或厂商支持,这些组织不光遵循X/Open组织定义的行业技术标准,也参与到标准的制定。
故障分析
单点故障
协调者出错,事务会失败。
集群部署即可。
阻塞资源
占用数据库连接,性能低。
保持连接是为了第一阶段和第二阶段使用的是同一个事务,确保提交或回滚事务,只需将操作前、后的数据落库记录,就能将当前的连接放弃了。
将连接释放掉,就能保证系统的性能了。
提交阶段什么都不用做,因为数据库已有操作后记录。
回滚也可以,因为已经记录过操作前的记录。
数据不一致
二阶段出错,数据不一致
适用场景
适合单块应用中,跨多库的分布式事务,而且因其严重依赖DB层面解决事务,所以效率很低,不适合高并发场景。
互联网公司基本都不用,因某个系统内部如果出现跨多库的操作,是不合规的。现在的微服务,一个大的系统分成几十甚至上百个服务。一般规约每个服务只能操作自己对应的一个数据库。
如果你要操作别的服务对应的库,不允许直连别的服务的库。要操作别人的服务的库,必须通过调用别的服务的接口。
主流的开源XA分布式事务解决方案