多从库时半同步复制不工作的BUG分析

[复制链接]
查看11 | 回复2 | 2015-3-4 14:52:11 | 显示全部楼层 |阅读模式
作者:胡呈清、黄炎
阅读原文
问题描述
MySQL版本:5.7.16,5.7.17,5.7.21
存在多个半同步从库时,如果参数 rpl_semi_sync_master_wait_for_slave_count=1,启动第1个半同步从库时可以正常启动,启动第2个半同步从库后有很大概率 slave_io_thread 停滞(复制状态正常,Slave_IO_Running: Yes,Slave_SQL_Running: Yes,但是完全不同步主库 binlog )

复现步骤
1. 主库配置参数如下:


1.png (14.12 KB, 下载次数: 38)
下载附件
2018-5-29 16:17 上传

2. 启动从库A的半同步复制 start slave,查看从库A复制正常
3. 启动从库B的半同步复制 start slave,查看从库B,复制线程正常,但是不同步主库 binlog


分析过程
首先弄清楚这个问题 ,需要先结合MySQL其他的一些状态信息,尤其是主库的 dump 线程状态来进行分析:
1. 从库A启动复制后,主库的半同步状态已启动:


2.png (11.65 KB, 下载次数: 38)
下载附件
2018-5-29 16:18 上传

再看主库的 dump 线程,也已经启动:


3.png (50.79 KB, 下载次数: 41)
下载附件
2018-5-29 16:19 上传

再看主库的 error log,也显示 dump 线程(21824)启动成功,其启动的半同步复制:


4.png (32.25 KB, 下载次数: 822)
下载附件
2018-5-29 16:20 上传

2. 从库B启动复制后,主库的半同步状态,还是只有1个半同步从库 Rpl_semi_sync_master_clients=1:


5.png (11.64 KB, 下载次数: 37)
下载附件
2018-5-29 16:21 上传

再看主库的 dump 线程,这时有3个 dump 线程,但是新起的那两个一直为 starting 状态:


6.jpg (61.72 KB, 下载次数: 38)
下载附件
2018-5-29 16:22 上传



7.jpg (31.79 KB, 下载次数: 33)
下载附件
2018-5-29 16:23 上传

再看主库的 error log,21847 这个新的 dump 线程一直没起来,直到1分钟之后从库 retry ( Connect_Retry 和 Master_Retry_Count 相关),主库上又启动了1个 dump 线程 21850,还是起不来,并且 21847 这个僵尸线程还停不掉:


8.png (42.95 KB, 下载次数: 42)
下载附件
2018-5-29 16:24 上传

3. 到这里我们可以知道,从库Bslave_io_thread 停滞的根本原因是因为主库上对应的 dump 线程启动不了。
如何进一步分析线程调用情况?推荐使用 gstack 或者 pstack(实为gstack软链)来查看线程调用栈,其用法很简单:gstack
4. 看主库的 gstack,可以看到 24102 线程(旧的复制 dump 线程)堆栈:


9.jpg (155.68 KB, 下载次数: 39)
下载附件
2018-5-29 16:25 上传

可以看到 24966 线程(新的复制 dump 线程)堆栈:
两线程都在等待 Ack_Receiver 的锁,而线程 21875 在持有锁,等待select:


10.png (75.11 KB, 下载次数: 36)
下载附件
2018-5-29 16:26 上传


理论上 select 不应hang, Ack_receiver 中的逻辑也不会死循环,请教公司大神黄炎进行一波源码分析。
5.semisync_master_ack_receiver.cc 的以下代码形成了对互斥锁的抢占, 饿死了其他竞争者:


11.png (12.52 KB, 下载次数: 38)
下载附件
2018-5-29 16:27 上传

在 mysql_mutex_unlock 调用后,应适当增加其他线程的调度机会。
试验: 在 mysql_mutex_unlock 调用后增加 sched_yield();,可验证问题现象消失。

结论
从库 slave_io_thread 停滞的根本原因是主库对应的 dump thread 启动不了;
rpl_semi_sync_master_wait_for_slave_count=1 时,启动第一个半同步后,主库 ack_receiver 线程会不断的循环判断收到的 ack 数量是否 >= rpl_semi_sync_master_wait_for_slave_count,此时判断为 true,ack_receiver基本不会空闲一直持有锁。此时启动第2个半同步,对应主库要启动第2个 dump thread,启动 dump thread 要等待 ack_receiver 锁释放,一直等不到,所以第2个 dump thread 启动不了。
相信各位DBA同学看了后会很震惊,“什么?居然还有这样的bug...”,这里要说明一点,这个bug 触发是有几率的,但是几率又很大。这个问题已经由我司大神在1月份提交了 bug 和 patch:https://bugs.mysql.com/bug.php?id=89370,加上本人提交SR后时不时的催一催,昨天官方终于确认将修复在 5.7.23(官方最终另有修复方法,没采纳这个 patch,期待我司大神下期分析官方的修复代码)。
最后或许会有疑问“既然是概率,有没有办法降低概率呢?”,尤其是不具备及时升级版本条件的同学,欢迎评论区留言讨论~


回复

使用道具 举报

千问 | 2015-3-4 14:52:11 | 显示全部楼层
可重现度这么高吗,手动就可以触发,按理说这么明显的BUG应该很多人都遇到才对
回复

使用道具 举报

千问 | 2015-3-4 14:52:11 | 显示全部楼层
lujinke 发表于 2018-5-30 15:23
可重现度这么高吗,手动就可以触发,按理说这么明显的BUG应该很多人都遇到才对

复现很简单,文章分享后确实有很多朋友表示遇到过。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行