MySQL:8.0 从库 MTS并发的定时炸弹

作者简介:高鹏,笔名八怪。《深入理解MySQL主从原理》图书作者,同时运营个人公众号“MySQL学习”,持续分享遇到的有趣case以及代码解析!

一、问题描述

8.0.28以下的MTS可能在运行一段时间后出现hang死的情况,大概如下(模拟的):

MySQL:8.0 从库 MTS并发的定时炸弹-1

这个问题因此有一个2的31次方事务后触发的界限,实际上就是int类型的溢出导致,因此为定时炸弹,BUG如下:

  • https://bugs.mysql.com/bug.php?id=103636

这个BUG由印风提交,可以看出来跑了很久才触发,着实费劲,感谢大佬,否则真的很难知道为什么。

二、关于slave_preserve_commit_order的实现

实际上本参数主要控制的是从库的提交顺序和主库一致,其实现大概分为两个步骤

  • 首先是SQL线程分发事务的时候对顺序进行记录,因为分发是按照主库的binlog的顺序分发的因此可以理解为就是seqnumber的顺序。
  • worker线程选择事务执行是无序的,但是在提交的时候就需要保证顺序,这个时候根据SQL线程分发的顺序进行提交即可。

在8.0中主要通过一个全局的Commit_order_manager结构进行记录,其中有2个部分比较重要:

1. 一个全部worker线程信息的vecoter容器,有多少个worker线程容器中就有多少个元素,其中每个元素代表一个worker,叫做node,包含一些重点的成员变量如下:

  • m_commit_sequence_nr{NO_SEQUENCE_NR} 提交序列信息
  • value_type m_worker_id worker线程的id
  • MDL_context *m_mdl_context 包含MDL LOCK信息
  • memory::Aligned_atomic m_stage 状态信息

其中m_commit_sequence_nr和m_mdl_context是为唤醒worker线程准备的,这点和5.7不同,5.7中为全部唤醒然后每个worker循环判断,

while (queue_front() != worker->id)