一、场景问题
今天MySQL存储节点突然收到cpu持续100%的报警,持续时间长达数个小时。在控制台中通过show processlist查看当前进程,发现很多一模一样的SQL一直在运行,执行时间都超过数个小时。
二、疑问分析
一般对用户而言,都会有一个超时时间,接口不可能无限等待,如果一直等待体验也不好。所以都会设置一个超时时间,不管是前端请求的超时时间,亦或者是nginx的请求超时时间。但大家思考过这样一个场景没,当http的请求断开,那接口中涉及到的SQL是会继续执行,还是会断开链接呢?
三、寻根问题
答案是肯定的,因为这条SQL已经在数据库执行了,除非应用程序提交kill的命令,否则它会一直执行到超时为止。
那这个超时的时间主要由哪个字段决定呢?我们来看看MySQL5.7的官网说明:
MySQL5.7是由:max_execution_time决定,如果设置为0,那就是不做任何限制,会一直执行到天荒地老。。。
本文讨论的范围为select查询操作,MySQL为5.7,其它版本和类型不再本文的讨论范围之内。
四、解决方案
一般来说数据库层面的配置,我们普遍会设置一个可以容忍的最大值,因为要兼容所有的应用,不可能针对单独的应用或者接口来设置。那问题又来了,我们如何设置应用级别和接口级别的查询超时呢?
4.1 应用超时
我们以druid连接池为例,如果想要设置应用级别的查询超时,可以添加如下配置:
#SQL执行超时时间,针对所有CRUD
spring.datasource.druid.query-timeout=300
但这种配置有个弊端就是,它生效的范围不仅仅selete,包含CRUD所有种类型。
因为druid目前没有提供针对于select查询超时时间设置,如果想要单一类型生效可以做一些自己的封装。具体的封装方式一般有2种,一种是修改源代码,对特殊的select进行修改,另外一种是在mybatis层面,代理执行的时候对属于select类型的进行拦截修改。博主比较推荐第二种方式,代码的侵入性比较低,第一种后期如果对druid升级会有麻烦。
4.2 接口超时
接口级别的可以加上setQueryTimeout属性,给SQL查询设置一个超时时间。这个属性其实和应用全局的形式是一样的,但是他是作用在具体的一个SQL层面,我们只需要给select类型设置这个字段就可以了。
五、题外话【产品】
还有一点想跟大家分享的是,属于非技术层面的。像上面提到的那个问题,问什么线上会出现那么多一模一样的SQL,一直不断的再执行呢?因为用户一旦发现哪个操作很久都没有结果出来,就会不断的一直重试,然后情况越来越坏,这也是为什么一旦系统出现问题,就会马上爆发出来,直到系统宕机。所以在产品这个维度也需要进行些优化,做一些限制。
六、总结
今天给大家分享了三种设置SQL查询超时的方法:数据库max_execution_time、druid连接池的spring.datasource.druid.query-timeout、接口级别的setQueryTimeout。除了技术层面,产品设计方面也需要考虑权衡。今天的分享就到这边了,林老师代码学编程,今天你学习了嘛。
公众号:林老师带你学编程
网站:www.wolzq.com
代码无非增删改查,关注老师给你Coding
林老师带你学编程:http://www.wolzq.com