MySQL · 捉虫动态 · GTID下slave_net_timeout值太小问题

背景

官方 5.6 最新版本 5.6.24 有这样一个bugfix,当使用 GTID 协议进行复制,并且备库的 slave_net_timeout 值设置太小的话,备库的 slave io 线程会卡住,同时主库上的 binlog dump 线程数一直在涨,官方的bug地址 。

bug分析

首先说明下几个概念:
1. slave_net_timeout,这个变量控制备库 IO 线程的连接超时,如果IO线程在指定时间内没有从主库收到数据的话,就断开重连,默认值是3600秒;
2. heartbeat 事件,主库在dump线程空闲的时候(dump线程已经把所有binlog都发给备库了,主库还没有更新操作),就隔断时间发一个 heartbeat 给备库,以免备库超时断开,这个时间间隔是备库在 change master 时传过来的,在 rpl_slave.cc 中可以看到,默认取 min<float>(SLAVE_MAX_HEARTBEAT_PERIOD, (slave_net_timeout/2.0))
3. GTID 的 auto_position 协议,正是因为有了 GTID,才可以在 chang master 时不指定binlog坐标(filename, pos),但是主库dump线程在发给备库event的时候,还是需要从某个坐标开始的,只是这个过程通过 GTID 能自动搜出来罢了,关于如何找到要发送的第一个 binlog 可以看下之前的月报,找到后就需要从这个 binlog 开头开始扫描,找到第一个备库没有 GTID,然后开始发送。

问题的源头就出在 binlog 文件扫描上,官方 bug 的复现步骤中有这样一个条件,use 1GB binlog file size. stop slave at high binlog position (>800MB),就是说主库的 binlog 要比较大,并且备库 IO 线程要停在这个 binlog 比较靠后的位置。这样备库 start slave 连上的时候,主库的 dump 线程就要从这个 binlog 开头往后扫,找到开始发送 binlog 的位置,如果文件比较大的话,这个扫描时间就会比较长。heartbeat 事件是在主库空闲的时候才会发送的,在扫描过种中是不会检查要不要发 heartbeat 的,如果备库的 slave_net_timeout 设置比较小的话,超过时间还没收到一个事件,这时备库 IO 就会停下,然后重连。

IO 线程重连会导致另外一个问题,我们知道一个主库是可以拖多个备库的,但是对每个备库只能有一个连接,如果已经连上来的备库再发一个 dump 请求的话,主库就会把当前备库老的 IO 连接置为 killed,具体函数是 sql/rpl_master.cc 中的 kill_zombie_dump_threads,但这个 kill 只是置了标志位,还需要老的 dump 线程自己判断,然后退出。dump 线程 killed 状态检测只在切换 binlog 和空闲的时候,如果这个时候老的 dump 线程还在扫 binlog,即使已经是killed了也不会退出。而同时新的 dump 线程依然要从头扫 binlog,重复上面的过程,导致备库 IO 又超时重连,因此在主库上show processlist的话,会看到 dump 线程慢慢堆积起来。

下面的结果就是备库 slave_net_timeout 设置为10s的测试结果,可以看到相邻 dump 线程差不多间隔10s。

|  9 | root | localhost:46882 | NULL | Binlog Dump GTID |  159 | init  | NULL             |
| 10 | root | localhost:46929 | NULL | Binlog Dump GTID |  149 | init  | NULL             |
| 11 | root | localhost:46975 | NULL | Binlog Dump GTID |  138 | init  | NULL             |
| 12 | root | localhost:47023 | NULL | Binlog Dump GTID |  127 | init  | NULL             |
| 13 | root | localhost:47074 | NULL | Binlog Dump GTID |  115 | init  | NULL             |
| 14 | root | localhost:47143 | NULL | Binlog Dump GTID |  104 | init  | NULL             |
| 15 | root | localhost:47206 | NULL | Binlog Dump GTID |   93 | init  | NULL             |
| 16 | root | localhost:47255 | NULL | Binlog Dump GTID |   82 | init  | NULL             |
| 17 | root | localhost:47311 | NULL | Binlog Dump GTID |   71 | init  | NULL             |
| 18 | root | localhost:47362 | NULL | Binlog Dump GTID |   60 | init  | NULL             |
| 19 | root | localhost:47427 | NULL | Binlog Dump GTID |   49 | init  | NULL             |

如果在备库上 show slave status 的的话,会看到 IO 线程接收的 binlog 位点一直不更新,就像 hang 住了一样。

bug修复

出现这个问题是因为主库 dump 线程检测 heartbeat_period 和 thd->killed 的粒度太大,都是 binlog 文件级别的,因此官方的改法就是把两者的检测粒度降为event级别,dump 线程每 read 一个 event,都会检测是否该发送 heartbeat 了,同时 thd 的 killed 标志是否被置上,是的话就退出。
具体的patch可以看官方github

有了这个 patch 后,主库 dump 线程还是需要时间扫描binlog,备库的 IO 线程看起来依然像 hang 住一样,但是已经不会超时重连,主库的dump 线程也不会堆积。

时间: 2024-08-21 15:53:13

MySQL · 捉虫动态 · GTID下slave_net_timeout值太小问题的相关文章

MySQL内核月报 2014.09-MySQL· 捉虫动态·GTID 和 binlog_checksum

现象描述 在5.6主备环境下,主备都开启GTID-MODE,备库开启crc校验,主库不开.重启备库sql线程后,备库sql线程停止Last_Error显示:Relay log read failure: Could not parse relay log event entry. The possible reasons are: the master's binary log is corrupted(you can check this by running 'mysqlbinlog' on

MySQL · 捉虫动态 · order by limit 造成优化器选择索引错误

问题描述 bug 触发条件如下: 优化器先选择了 where 条件中字段的索引,该索引过滤性较好: SQL 中必须有 order by limit 从而引导优化器尝试使用 order by 字段上的索引进行优化,最终因代价问题没有成功. 复现case 表结构 create table t1( id int auto_increment primary key, a int, b int, c int, key iabc (a, b, c), key ic (c) ) engine = innod

MySQL内核月报 2014.09-MySQL· 捉虫动态·GTID 和 DELAYED

描述 这是一个MySQL 5.6才有的bug,影响包含最新版本.涉及到的概念有GTID.DELAYED. 现象 在5.6主备都开启GTID-MODE的时候,备库同步线程停止,且Last_SQL_Error显示"When @@SESSION.GTID_NEXT is set to a GTID, you must explicitly set it to a different value after a COMMIT or ROLLBACK. Please check GTID_NEXT var

MySQL · 捉虫动态·DROP DATABASE外键约束的GTID BUG

背景 MySQL的DDL没有被设计成事务操作,因此DDL操作是无法回滚的(像PgSQL把DDL也设计成事务操作,DDL就可以在执行成功后被回滚操作取消).这就会导致如果某个DDL语句内部被拆分为多个原子的DDL调用,那么这个DDL语句就不具备中途执行失败后回滚整个DDL语句的能力,也就是说,即使语句逻辑内的某个原子DDL调用失败了,也无法回滚已经完成的那些原子DDL调用. 问题描述 DROP DATABASE 就是一个例子,对于MySQL而言,DROP DATABASE 并非是一个原子DDL操作

MySQL · 捉虫动态 · Relay log 中 GTID group 完整性检测

bug背景 官方 5.7.6 版本对 gtid 有非常多的改进和bugfix,其中有一个 bugfix 是针对 relay log 中没有接收完整的 gtid 事务的.正常的relay log 中的 gtid 事务应该是像下面这样: 1. gtid event 2. query event (begin) 3. row event (write/update/delete) 4. query event (commit) 上面这 4 个 event 序列构成一个 group.因为 IO 线程从主

MySQL · 捉虫动态 · 信号处理机制分析

背景 在 AliSQL 上面有人提交了一个 bug,在使用主备的时候 service stop mysql 不能关闭主库,一直显示 shutting down mysql -,到底怎么回事呢,先来看一下 service stop mysql 是怎么停止数据库的.配置 MySQL 在系统启动时启动需要把 MYSQL_BASEDIR/support-files 目录下的脚本 mysql.sever 放到 /etc/init.d/ 目录下,脚本来控制 mysqld 的启动和停止.看一下脚本中的代码 :

MySQL · 捉虫动态 · MySQL 外键异常分析

外键约束异常现象 如下测例中,没有违反引用约束的插入失败. create database `a-b`; use `a-b`; SET FOREIGN_KEY_CHECKS=0; create table t1(c1 int primary key, c2 int) engine=innodb; create table t2(c1 int primary key, c2 int) engine=innodb; alter table t2 add foreign key(c2) referen

MySQL · 捉虫动态 · 唯一键约束失效

唯一键是数据库设计中常用的索引类型,主要用于约束数据,不允许出现重复的键值记录.可以想象,如果唯一键约束失效了,将可能产生可怕的逻辑错误.本文主要讨论下最近MySQL爆出来的两个唯一键约束失效导致二级索引corruption的问题. 问题一: 检查重复键加锁逻辑不当 影响版本:MySQL 5.6.21之前,5.6.12之后的版本 介绍分析 在5.6.12之前的版本中,当插入一条带唯一约束的记录时,如果表上已经存在了这条记录,或者有一条标记删除的相同键值记录时,就需要对这条记录加S GAP (类型

MySQL · 捉虫动态 · ORDER/GROUP BY 导致 mysqld crash

问题描述 表结构如下所示: show create table test\G Table: test Create Table: CREATE TABLE `test` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id2` varchar(50) DEFAULT NULL `id3` varchar(100) DEFAULT NULL `some_text` varchar(200) DEFAULT NULL `name` varchar(