MySQL 5.7的分布式事务支持举例分析

56次阅读
没有评论

共计 3161 个字符,预计需要花费 8 分钟才能阅读完成。

本篇内容主要讲解“MySQL 5.7 的分布式事务支持举例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让丸趣 TV 小编来带大家学习“MySQL 5.7 的分布式事务支持举例分析”吧!

分布式事务通常采用 2PC 协议,全称 Two Phase Commitment Protocol。该协议主要为了解决在分布式数据库场景下,所有节点间数据一致性的问题。在分布式事务环境下,事务的提交会变得相对比较复杂,因为多个节点的存在,可能存在部分节点提交失败的情况,即事务的 ACID 特性需要在各个数据库实例中保证。总而言之,在分布式提交时,只要发生一个节点提交失败,则所有的节点都不能提交,只有当所有节点都能提交时,整个分布式事务才允许被提交。

分布式事务通过 2PC 协议将提交分成两个阶段

prepare;

commit/rollback

第一阶段的 prepare 只是用来询问每个节点事务是否能提交,只有当得到所有节点的“许可”的情况下,第二阶段的 commit 才能进行,否则就 rollback。需要注意的是:prepare 成功的事务,则必须全部提交。

MySQL 分布式事务

一直以来,MySQL 数据库是支持分布式事务的,但是只能说是有限的支持,具体表现在:

已经 prepare 的事务,在客户端退出或者服务宕机的时候,2PC 的事务会被回滚

在服务器故障重启提交后,相应的 Binlog 被丢失

上述问题存在于 MySQL 数据库长达数十年的时间,直到 MySQL-5.7.7 版本,官方才修复了该问题。虽然 InnoSQL 早已在 5.5 版本修复,但是对比官方的修复方案,我们真的做的没有那么的优雅。下面将会详细介绍下该问题的具体表现和官方修复方法,这里分别采用官方 MySQL-5.6.27 版本 (未修复) 和 MySQL-5.7.9 版本 (已修复) 进行验证。

先来看下存在的问题,我们先创建一个表如下:

create table t(
 id int auto_increment primary key, 
 a int
)engine=innodb;

对于上述表,通过如下操作进行数据插入:

mysql  XA START  mysql56 
mysql  INSERT INTO t VALUES(1,1);
mysql  XA END  mysql56 
mysql  XA PREPARE  mysql56

通过上面的操作,用户创建了一个分布式事务,并且 prepare 没有返回错误,说明该分布式事务可以被提交。通过命令 XA RECOVER 查看显示如下结果:

mysql  XA RECOVER;
+----------+--------------+--------------+---------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+---------+
| 1 | 7 | 0 | mysql56 |
+----------+--------------+--------------+---------+

若这时候用户退出客户端后重连,通过命令 xa recover 会发现刚才创建的 2PC 事务不见了。即 prepare 成功的事务丢失了,不符合 2PC 协议规范!!!

产生上述问题的主要原因在于:MySQL-5.6 版本在客户端退出的时候,自动把已经 prepare 的事务回滚了,那么 MySQL 为什么要这样做?这主要取决于 MySQL 的内部实现,MySQL-5.7 以前的版本,对于 prepare 的事务,MySQL 是不会记录 binlog 的(官方说是减少 fsync,起到了优化的作用)。只有当分布式事务提交的时候才会把前面的操作写入 binlog 信息,所以对于 binlog 来说,分布式事务与普通的事务没有区别,而 prepare 以前的操作信息都保存在连接的 IO_CACHE 中,如果这个时候客户端退出了,以前的 binlog 信息都会被丢失,再次重连后允许提交的话,会造成 Binlog 丢失,从而造成主从数据的不一致,所以官方在客户端退出的时候直接把已经 prepare 的事务都回滚了!

官方的做法,貌似干得很漂亮,牺牲了一点标准化的东西,至少保证了主从数据的一致性。但其实不然,若用户已经 prepare 后在客户端退出之前,MySQL 发生了宕机,这个时候又会怎样?

MySQL 在某个分布式事务 prepare 成功后宕机,宕机前操作该事务的连接并没有断开,这个时候已经 prepare 的事务并不会被回滚,所以在 MySQL 重新启动后,引擎层通过 recover 机制能恢复该事务。当然该事务的 Binlog 已经在宕机过程中被丢失,这个时候,如果去提交,则会造成主从数据的不一致,即提交没有记录 Binlog,从上丢失该条数据。所以对于这种情况,官方一般建议直接回滚已经 prepare 的事务。

以上是 MySQL-5.7 以前版本 MySQL 在分布式事务上的各种问题,那么 MySQL-5.7 版本官方做了哪些改进?这个可以从官方的 WL#6860 描述上得到一些信息,我们还是本着没有实践就没有发言权的态度,从具体的操作上来分析下 MySQL-5.7 的改进方法:

还是以上面同样的表结构进行同样的操作如下:

mysql  XA START  mysql57 
mysql  INSERT INTO t VALUES(1,1);
mysql  XA END  mysql57 
mysql  XA PREPARE  mysql57 通过上面的操作,明显发现在 prepare 以后,从 XA START 到 XA PREPARE 之间的操作都被记录到了 Master 的 Binlog 中,然后通过复制关系传到了 Slave 上。也就是说 MySQL-5.7 开始,MySQL 对于分布式事务,在 prepare 的时候就完成了写 Binlog 的操作,通过新增一种叫

当然仅靠这一点是不够的,因为我们知道 Slave 通过 SQL thread 来回放 Relay log 信息,由于 prepare 的事务能阻塞整个 session,而回放的 SQL thread 只有一个(不考虑并行回放),那么 SQL thread 会不会因为被分布式事务的 prepare 阶段所阻塞,从而造成整个 SQL thread 回放出现问题?这也正是官方要解决的第二个问题:怎么样能使 SQL thread 在回放到分布式事务的 prepare 阶段时,不阻塞后面 event 的回放?其实这个实现也很简单(在 xa.cc::applier_reset_xa_trans),只要在 SQL thread 回放到 prepare 的时候,进行类似于客户端断开连接的处理即可(把相关 cache 与 SQL thread 的连接句柄脱离)。最后在 Slave 服务器上,用户通过命令 XA RECOVER 可以查到如下信息:

mysql  XA RECOVER;
+----------+--------------+--------------+---------+
| formatID | gtrid_length | bqual_length | data |
+----------+--------------+--------------+---------+
| 1 | 7 | 0 | mysql57 |
+----------+--------------+--------------+---------+

至于上面的事务什么时候提交,一般等到 Master 上进行 XA COMMIT  ‘mysql57’后,slave 上也同时会被提交。

到此,相信大家对“MySQL 5.7 的分布式事务支持举例分析”有了更深的了解,不妨来实际操作一番吧!这里是丸趣 TV 网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-19发表,共计3161字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)