Mysql5.7 XA事物的改进
问题来源
在使用otter数据同步项目的时候,由于业务方使用了XA事物,预发环境又是mysql5.7版本,导致XA命令不支持,当改造完otter支持XA命令解析测试的时候,测试发现XA的Rollback数据居然也同步成功了,最终定位到原来mysql在5.7版本之后,增加了在XA Prepare之后就会将binlog生成,发生XA Rollback只是发送命令,无法得知需要更新的数据信息,这就导致到otter数据不一致问题。以下是整理的关于mysql 5.7关于XA的知识点。
什么是XA
XA(分布式事务)规范主要定义了(全局)事务管理器(TM: Transaction Manager)和(局部)资源管理器(RM: Resource Manager)之间的接口。XA为了实现分布式事务,将事务的提交分成了两个阶段:也就是2PC (tow phase commit),XA协议就是通过将事务的提交分为两个阶段来实现分布式事务。
两阶段
1)prepare 阶段
事务管理器向所有涉及到的数据库服务器发出prepare”准备提交”请求,数据库收到请求后执行数据修改和日志记录等处理,处理完成后只是把事务的状态改成”可以提交”,然后把结果返回给事务管理器。即:为prepare阶段,TM向RM发出prepare指令,RM进行操作,然后返回成功与否的信息给TM。
2)commit 阶段
事务管理器收到回应后进入第二阶段,如果在第一阶段内有任何一个数据库的操作发生了错误,或者事务管理器收不到某个数据库的回应,则认为事务失败,回撤所有数据库的事务。数据库服务器收不到第二阶段的确认提交请求,也会把”可以提交”的事务回撤。如果第一阶段中所有数据库都提交成功,那么事务管理器向数据库服务器发出”确认提交”请求,数据库服务器把事务的”可以提交”状态改为”提交完成”状态,然后返回应答。即:为事务提交或者回滚阶段,如果TM收到所有RM的成功消息,则TM向RM发出提交指令;不然则发出回滚指令。
外部与内部XA
MySQL中的XA实现分为:外部XA和内部XA。前者是指我们通常意义上的分布式事务实现;后者是指单台MySQL服务器中,Server层作为TM(事务协调者,通常由binlog模块担当),而服务器中的多个数据库实例作为RM,而进行的一种分布式事务,也就是MySQL跨库事务;也就是一个事务涉及到同一条MySQL服务器中的两个innodb数据库(目前似乎只有innodb支持XA)。内部XA也可以用来保证redo和binlog的一致性问题。
2.2. redo与binlog的一致性问题
我们MySQL为了兼容其它非事务引擎的复制,在server层面引入了 binlog, 它可以记录所有引擎中的修改操作,因而可以对所有的引擎使用复制功能; 然而这种情况会导致redo log与binlog的一致性问题;MySQL通过内部XA机制解决这种一致性的问题。
第一阶段:InnoDB prepare, write/sync redo log;binlog不作任何操作;
第二阶段:包含两步,1> write/sync Binlog; 2> InnoDB commit (commit in memory);
当然在5.6之后引入了组提交的概念,可以在IO性能上进行一些提升,但总体的执行顺序不会改变。
当第二阶段的第1步执行完成之后,binlog已经写入,MySQL会认为事务已经提交并持久化了(在这一步binlog就已经ready并且可以发送给订阅者了)。在这个时刻,就算数据库发生了崩溃,那么重启MySQL之后依然能正确恢复该事务。在这一步之前包含这一步任何操作的失败都会引起事务的rollback。
第二阶段的第2大部分都是内存操作,比如释放锁,释放mvcc相关的read view等等。MySQL认为这一步不会发生任何错误,一旦发生了错误那就是数据库的崩溃,MySQL自身无法处理。这个阶段没有任何导致事务rollback的逻辑。在程序运行层面,只有这一步完成之后,事务导致变更才能通过API或者客户端查询体现出来。
MySQL 5.7 XA可靠性改进
MySQL 5.7解决了 xa prepare了的事务的严格持久化问题,也就是在session断开和实例崩溃重启情况下这些事务不丢,同时在 xa prepare ‘xid1’返回之前XA事务也会同步到备库。下面将通过在5.6和5.7上分别执行xa prepare并对binlog event进行分析 来演示这个改进。
断开连接对xa prepare的事务影响
在5.6和5.7上分别执行如下sql然后断开连接,再重新连接使用的xa recover验证 XA 事务是否回滚了。
1 | xa start 'xid1'; |
在 5.6 的版本上将返回空的结果,在 5.7 的版本上返回:
1 | mysql> xa recover; |
说明断开连接后 5.7的prepare了的xa事务没有丢失。
XA 事务的 Binlog events 异同
在5.6和5.7上分别执行如下事务,然后用 show binlog events 查看两者binlog的不同:
1 | xa start 'xid1'; |
5.6的结果:
1 | mysql-bin.000001 | 304 | Gtid | 3706 | 352 | SET @@SESSION.GTID_NEXT= 'uuid:2' |
5.7的结果:
1 | mysql-bin.000001 | 544 | Gtid | 3707 | 592 | SET @@SESSION.GTID_NEXT= 'uuid:3' |
可以看到 MySQL 5.6 XA 事务和普通事务的binlog是一样的,并没有体现 xa prepare。而到了 MySQL 5.7 XA 事务的binlog和 普通的事务是完全不同的,XA Prepare有单独的Log event类型,有自己的Gtid,当开启semi-sync的情况下,MySQL 5.7 执行 XA prepare 时会等备库回复后才返回结果给客户端,这样XA prepare执行完就是安全的。
通过以上分析可以看出 MySQL 5.7在XA事务安全性方面做了很大的改进。
事物相关命令
普通事物命令
1 | set autocommit = 0; |
XA事物命令
1 | SHOW VARIABLES like '%support_xa%'; |
binlog相关命令
1 | -- 查看当前的binlog信息 |