问题背景
某环境上有一组Percona MySQL 5.7.23-23的半同步主从. 我们采用Prometheus监控框架,按其接口规范自研了独立的exporter用于监控数据采集.类似于mysqld_exporter,工作方式大致为:
* 开启一个http端口
* 采集本机MySQL状态(简单的show语句)
* 将状态发布到http端口.
exporter工作模式比较简单,理论上不会对数据库造成严重影响.
但在数据库节点上部署启动exporter后,发现数据库的连接状态异常,对业务产生了一定影响.
1.检查连接可用性
1.1通过TCP检查连接可用性 — 新连接无法建立!!
1.2通过Socket检查连接可用性 — 仍然无法建立连接
2.已连接的业务报错查询超时
3.检查mysql-error.log — 没有任何内容
4.检查系统日志 — 没有任何相关信息
5.观察系统资源占用,无异常占用
为何再正常不过的状态采集会导致如此大的问题? 由于日志中已经无法继续判断原因. 所以迅速采集调用栈信息:
问题分析
梳理exporter对数据库发起的SQL语句:
* show global variables
* show global status
* show master status
* show slave status
通过SQL语句列表和调用栈信息,大致可以观测出MySQL内部出现了Mutex死锁.
死锁关系如下:
1.SHOW GLOBAL VARIABLES
持有LOCK_global_system_variables, 等待LOCK_log
2.SLAVE SQL THREAD
持有LOCK_log, 等待LOCK_status
3.SHOW GLOBAL STATUS
持有LOCK_status, 等待LOCK_global_system_variables
我们来看下每部分的调用栈信息.
源码部分基于Percona MySQL 5.7.23-23
SHOW GLOBAL VARIABLES
关键代码
SLAVE SQL THREAD
关键代码
SHOW GLOBAL STATUS
关键代码
问题1: 新连接为什么无法建立?
连接请求时的调用栈
连接的初始化需要LOCK_global_system_variables, 此mutex被占用.导致新连接无法初始化
新连接请求发起时一直被挂起,没有任何响应,现象如下:
问题2:查询操作为什么超时?
查询操作的调用栈
SQL查询时也需要LOCK_global_system_variables, 此mutex被占用,导致查询被阻塞.
问题结论
经过一轮分析,我们基本找到问题原因. 但心中还存在疑问.
1. LOCK_global_system_variables是热点的mutex,大概率会出现争用,但不至于死锁.
2. show global variables操作为何需要LOCK_log?
3. 这样常用的查询真的会稳定导致数据库死锁么?
进一步搜索,发现社区已有类似的缺陷反馈:
https://bugs.mysql.com/bug.php?id=92108
在5.7.23版本中,读取这两个参数需要LOCK_log.从而会导致各种各样的死锁(bug正文中提到了另外一种场景).
在5.7.25版本以后,这两个参数的读取使用了另外一种影响更低的lock,从而避免了本文以及bug中提到的死锁.
问题处理
对于环境中出现死锁的这一组Percona MySQL 5.7.23-23半同步主从,我们使用5.7.25-28版本进行了升级.升级后此问题消失.至此可以基本确定导致问题的根本原因