遇到故障,我们往往想的是如何解决这个故障,而不是从故障的根本去思考出现这个故障的原因?这样的结果,只能使我们得到了鱼,失去了渔。今天,我们就来分享一个由使用DB堵塞故障引发的思考案例。
故障描述
今天一个朋友遇到数据库遇到一个严重的故障,故障环境如下:
MYSQL 5.6.16
RR隔离级别
GITD关闭
表现如下:
使用db不能进入数据库
显示表状态不能查询到表信息
模式。processlist来看有大量的等待表元数据锁
情急之下他杀掉了一大堆线程后发现还是不能恢复,最后杀掉了一个没有及时提交的事物才恢复正常。也仅仅留下了如下图的一个截图:
故障信息提取
还是回到上图,我们可以归纳一下语句类型如下:
1, <强>创建表,选择B 强>
其状态为
发送数据 2, <强>删除表强>
其状态为等待表元数据锁
3 <强> SELECT *从强>
其状态为等待表元数据锁
, <强>显示表状态(就像“A”) 强>
其状态为等待表元数据锁
信息分析
要分析出这个案列其实不太容易因为他是MYSQL层MDL锁和RR模式innodb行锁的一个综合案列,并且我们要对schema.processlist的状态比较敏感才行。
建议先阅读我的如下文章来学习MDL锁:
本节关于MDL锁的验证使用下面两种方式:
<强>方式一强>:笔者在MDL锁源码加锁函数处加日志输出,如果要分析各种语句加MDL锁的类型还只能用这种方式,因为MDL锁加锁往往一闪而过,performance_schema。metadata_locks没有办法观察到。
<强>方式二强>:处于堵塞情况下使用5.7版本的performance_schema.metadata_locks观察。
在P_S中打开mdl监测方法如下:
<强>一、关于创建表的选择对B表发送数据的分析强>
关于发送数据这个状态其实可以代表很多含义,从我现有的对的了解,这是MYSQL上层对选择类型语句的这类语句在INNODB层和MYSQL层进行数据交互的时候一个统称,所以出现它的可能包含:
确实需要访问数据量特别大,可能需要优化。
由于INNODB层的获取行锁需要等待,比如我们常见的选择更新。
同时我们还需要注意在RR模式下选择B这一部分加锁方式和插入……选择是一致的参考不再赘述:
从他反应的情况因为他在最后杀掉了一个长期的未提交的事物所以他因为是情况2。并且整个创建表,选择B语句由于B表上某些数据库被上了锁而不能获取,导致整个语句处于发送数据状态下。
<强>二,关于显示表状态(就像“A”)等待表元数据锁的分析强>
这是本案例中最重要的一环,显示表状态(就像“A”)居然被堵塞其状态为等待表元数据锁并且注意这里是表因为MDL锁类型分为很多。我在MDL介绍的那篇文章中提到了desc一个表的时候会上MDL_SHARED_HIGH_PRIO (SH),其实在显示表状态的时候也会对本表上MDL_SHARED_HIGH_PRIO (SH)。
<强>方式一强>
<强>方式二强>
两种方式都能观察到MDL_SHARED_HIGH_PRIO (SH)的存在并且我模拟的是处于堵塞情况下的。
但是MDL_SHARED_HIGH_PRIO (SH)是一个优先级非常高的一个MDL锁类型表现如下:
兼容性:
阻塞队列优先级:
其被堵塞的条件除了被MDL_EXCLUSIVE (X)堵塞没有其他的可能。那么这就是一个非常重要的突破口。
<强>三,关于创建表,选择B对表的加锁的MDL分析