MySQL · 捉虫动态 · 连接断开导致XA事务丢失

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
简介: 我们看到在MySQL 5.7版本里大量遗留很多年的bug都被fix掉了,bug#12161就是其中一个,该bug在2005年第一次report到Bug list上,十年之后终于在MySQL 5.7.7 第一个RC版本被fix了。 Bug描述 当我们显式开启一个XA事务,执行操作,并完成XA PR

我们看到在MySQL 5.7版本里大量遗留很多年的bug都被fix掉了,bug#12161就是其中一个,该bug在2005年第一次report到Bug list上,十年之后终于在MySQL 5.7.7 第一个RC版本被fix了。

Bug描述

当我们显式开启一个XA事务,执行操作,并完成XA PREPARE后,如果Kill session或者主动断开再重连执行XA RECOVER,之前的这个XA事务就会直接丢失掉了。

例如:

mysql> XA BEGIN 'abc';
Query OK, 0 rows affected (0.00 sec)
   
mysql> INSERT INTO t1 VALUES (1,2,3);
Query OK, 1 row affected (0.00 sec)
   
mysql> XA END 'abc';
Query OK, 0 rows affected (0.00 sec)
   
mysql> XA PREPARE 'abc';
Query OK, 0 rows affected (0.00 sec)
   
mysql> Ctrl-C -- exit!
Aborted
   
mysql> XA RECOVER;
Empty set (0.00 sec)

有趣的是,如果在XA PREPARE后把实例KILL掉,是可以通过XA RECOVER恢复的:

mysql> XA RECOVER;
+----------+--------------+--------------+------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+------+
| 1 | 3 | 0 | abc |
+----------+--------------+--------------+------+
1 row in set (0.00 sec)
   
mysql> XA COMMIT 'abc';
Query OK, 0 rows affected (0.00 sec)

虽然实例异常重启可以恢复事务,但引入的另外一个问题是:事务变更的binlog丢失,导致主备数据不一致。

bug产生的原因也很简单:在退出session时,线程总是会去无条件的回滚掉自己尚未提交的事务。

官方修复

持久化

为了解决这个问题,将XA的两阶段记录到了Binlog中;

对于上文描述的序列,当执行到XA PREPARE时,记录第一阶段的binlog,如下:

Query event : XA START X'616263',X'’,1  // 这里的'616262'即是'abc'的十六进制编码
Table_map event
Write_rows event
Query event:XA END X'616263',X'',1
XA_prepare event: XA PREPARE X'616263',X'’,1

这时候该XA事务同时在InnoDB层(事务处于Prepare状态,Redo持久化到磁盘)和Server层都有持久化信息。

其中XA_PREPARE事件是新引入的事件类型(内部类为XA_prepare_event),以后版本升级需要注意到这个低版本不兼容事件。

然后再执行XA COMMIT ‘abc’,产生新的事件:

Query event:XA COMMIT X'616263',X'',1

如果执行XA ROLLBACK,则记录:

Query event:XA ROLLBACK X'616263',X'',1

由于XA PREPARE和XA COMMIT是分开执行的,因此在这两个事件中间可能存在别的事务,备库复制线程需要处理这种情况。

为了实现XA PREPARE写binlog,对binlog_prepare进行了扩展,这里会调用mysql_bin_log.commit, 将cache中的binlog刷到文件中。

Tips:XID可以包含三个部分:gtrid, [, bqual [, format ID]],其中gtrid是必选的,表示全局标识,bqual是分支标识,默认为空’‘,format ID是一个unsigned整型,默认值为1,在上例中,我们只指定了gtrid为’abc’,因此bqual段和format ID均为默认值。更具体的描述参考官方文档

如何恢复

当会话断开时(例如kill session或者一次干净的shutdown/restart操作),我们必须要能恢复该事务,之前的逻辑是在cleanup时,直接回滚所有的活跃事务。在新版本中,对XA PREPARE的事务做了特殊处理(THD::cleanup),如果处于Prepare状态,就将事务的in_recovery设置为TRUE,并更新到hash表transaction_cache中(transaction_cache_detach),该hash表用于维护所有XA事务。

对于非XA的活跃事务,在会话断开时,依然采用回滚策略。

当重连客户端后,我们可以直接执行 XA COMMIT ‘abc’,这时候会通过XID关键字去搜索transaction_cache并将对应的事务提交掉。

同时BINLOG的状态要保持一致,如果会话断开前的XA PREPARE没有记录Binlog, 重连后执行XA COMMIT也不应该记录。

备库复制

由于XA PREPARE和XA COMMIT是分开记录的,当碰到XA COMMIT时,备库采用等待之前的事务全部完成,然后再执行的方式(相当于退化到串行)。

另外,我们知道在一个正常的会话过程中,总是为其cache一个事务对象,新的事务会重用这个事务对象,避免多次分配;而XA事务的COMMIT和PREPARE是分离的,需要为XA事务单独分配事务对象。因此复制线程执行XA START时,将其拥有的事务对象临时保存起来(detach_native_trx),当执行到XA_prepare_log_event事件时,再将其恢复给复制线程,同时XA事务对象关闭read view,将is_recovered设置为TRUE(函数innodb_replace_trx_in_thd)。

随后复制线程在执行到XA COMMIT时直接根据XID找到对应的XA事务进行提交。

参考:

WL#6860 Binlogging XA-prepared transaction
Github:git show f4c37f7aea732763947980600c6882ec908a54a0
MySQL 5.7.7-RC

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
13天前
|
存储 SQL 关系型数据库
MySQL的事务隔离级别
【10月更文挑战第17天】MySQL的事务隔离级别
89 43
|
9天前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
|
26天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1601 14
|
13天前
|
SQL JavaScript 关系型数据库
node博客小项目:接口开发、连接mysql数据库
【10月更文挑战第14天】node博客小项目:接口开发、连接mysql数据库
|
18天前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
19天前
|
Java 关系型数据库 MySQL
【编程基础知识】Eclipse连接MySQL 8.0时的JDK版本和驱动问题全解析
本文详细解析了在使用Eclipse连接MySQL 8.0时常见的JDK版本不兼容、驱动类错误和时区设置问题,并提供了清晰的解决方案。通过正确配置JDK版本、选择合适的驱动类和设置时区,确保Java应用能够顺利连接MySQL 8.0。
90 1
|
2月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
370 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
22天前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
35 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库
|
21天前
|
SQL JavaScript 关系型数据库
Node.js 连接 MySQL
10月更文挑战第9天
17 0
|
2月前
|
SQL 关系型数据库 MySQL
MySQL C连接与使用
【9月更文挑战第21天】在 MySQL 中,可以通过 C 语言连接和操作数据库。首先需安装 MySQL 服务器及 C 开发库,然后在程序中包含必要头文件,初始化连接对象,并使用实际参数建立连接。执行 SQL 语句时,需替换表名等变量,获取并遍历结果集。最后,释放资源并关闭连接。过程中应注意错误处理、内存管理和安全性,以及性能优化。此方式适用于高效数据存储和检索的应用程序。

相关产品

  • 云数据库 RDS MySQL 版