从杀慢查询入手来预防 MySQL 雪崩的办法

作者介绍

王竹峰

去哪儿网数据库总监

擅长数据库开发、数据库管理及维护,一直致力于 MySQL 数据库源码的研究与探索,对数据库原理及实现有深刻的理解。曾就职于达梦数据库,从事多年数据库内核开发工作,后转战人人网,任职数据库工程师,目前在去哪儿网负责 MySQL 源码研究与运维、数据库管理和自动化运维平台设计开发及实践工作,是 Inception 开源项目及《MySQL运维内参》的作者, MySQL 方向的 Oracle ACE。

一、背景

慢查询在 MySQL 数据库管理中,已经是再熟悉不过的事情了,只要我们在使用 MySQL,那慢查询就会一直存在下去,因为不管是业务 APP,还是 MySQL,他们的状态都是动态变化的,在这个动态的服务中,可能经常遇到的问题是,某几个指标的变化形成了共振效应,进而导致本来不慢的查询语句变成慢查询,本来可以走二级索引并很快返回的语句变成了全表扫描,这还不止,可能这种影响范围会继续进一步扩大,导致整个实例或者集群被打死,出现一些“被影响”的很慢的查询语句,从而产生了我们通常意义上的“雪崩效应”,终导致的是由慢查询导致的数据库故障。

但这样的故障,真是由慢查询导致的么?这个我认为不一定,具体原因很难穷尽,但在一个动态变化的环境中,多个因素导致了共振效应,进一步导致雪崩现象,这应该是确定的。面对这样的问题,我们应该怎么解决,或者提前避免呢?可能很多人会认为因为是共振导致的,是不是要去查到具体共振因素,这样问题就解决了?我想说的是,这种解决办法有时候可以解决问题,但通常问题发生之后,共振的场景已经不见了,我们此时见到的只有慢查询,除非有很全面的日志,不然这种方法不具有可操作性。

二、解决办法

对于上面出现的问题,我们不能从原因上解决慢查询,那么是不是可以考虑从结果上去解决呢?结果就是慢查询,也就是说,我们是不是只需要把慢查询消灭了,就可以把相应的雪崩避免了?

答案是肯定的,我们可以想想,如果雪崩出现的时候,也就是雪崩引起的故障出现的时候,我们是咋处理故障的?一般情况下,也是通过不断杀慢查询的方式来解决问题的,让拥堵消失,拥堵消失之后,数据库本身的状态就平静了,业务就可以继续有条不紊的访问了,所以用同样的原理,我们如果能在共振出现之后,批慢查询出现的时候,将其杀掉,这样可能就能有效避免后续的雪崩效应,这样的推论是没有问题的。

三、杀慢查询的方式

很多人想到了上面的解决方案,就是将慢查询杀掉,但在慢查询一开始出现的时候,压力还不大,数据库可能可以扛过去,DBA 可能并不会注意到这个数据库“将来”会出现问题,所以并不会选择杀掉,只有恶化了,或者已经出现小面积的雪崩了,才会选择去杀掉慢查询,但此时杀掉的逻辑很简单————只要是查询的,时间大于多少的,可能就会被杀掉了,大不了再多加几个过滤条件而已,比如状态是 statistics,方式是大同小异的,但这样的方式有很多弊端,我列举如下:

那还有没有一种办法,在有效避免上述所有问题的同时,还能解决慢查询带来的各种问题呢?答案是有的。

四、解铃还须系铃人

我们做这个事情,需要换一种思路去考虑问题,语句是开发写的,语句是从应用程序访问过来的,那只有应用端或者开发才了解 SQL 语句的情况,包括需要执行多久(SQL 语句的超时时间设置),或者说执行多长时间,就肯定是有问题的,而语句相关的其它参数,他们是不一定能知道的,他们在只知道这样的信息的情况下,如何去杀慢查询呢?有三种办法:

  • 注册制

DBA 开发一个“强大”的系统,用来注册 SQL 语句,指标包括数据库地址,大执行时间,其它都可以不要,有了这个系统,DBA 可以在每一个数据库上面搞一个 Agent,不断地去访问这个配置库,同时去看 Processlist 中的信息,如果语句和时间都能匹配得上,就杀掉,这种办法当然比上面的办法强多了,至少执行杀的动作是有决策根据的,这种决策根据是建立在业务对自己语句的了解以及对健康度的风险把控之上的,这样就可以有效的避免意外情况相互拥堵导致的数据库雪崩问题,至少在雪崩之前就可以将这个拥堵的状态解决掉。但这种办法也是有问题的,久而久之这个配置库会非常大,因为极限条件下,线上出现的每个 SQL 语句都会出现在这里,这个杀慢查询的 Agent 就没办法良好运行了,并且匹配的是整个 SQL 语句,涉及到模式处理问题,以及很重的字符串比较的问题,效率不能保证。所以,这种办法也是不可行的。