把生产中的慢SQL从6000ms,一番折腾后运行只要40ms

慢SQL报警

邮件接收到了慢SQL报警,一看时间吓一跳,竟然将近26s,看看是什么妖魔鬼怪?

由于涉及到生产,表名经过处理,并不是真实表名。。。

com.alibaba.druid.filter.stat.StatFilter | slow sql 25116 millis. SELECT count(1) FROM result oqr INNER JOIN o_query oq ON oq.sid = oqr.query_id WHERE 1 = 1 and oqr.status = 1 and oq.type not in (2,6) AND oqr.create_time >= ? AND oqr.create_time <= ?["2019-09-11 00:00:01","2019-10-11 23:59:59"]

开始分析

执行计划

初看就是两个表内连接,那看一看执行计划吧。。。

平台输出执行计划格式有问题,凑合看吧。


原始SQL执行计划

有一个全表扫描,而且行数很多啊,难怪很慢!

生产环境通过页面跳板的方式查询数据库,将这条SQL在页面执行的时候,第一次没有查出结果,因为平台有执行时间限制(20s),也意味着这条SQL超过20s还没有执行处理结果。

后来又执行了几次,通过执行记录来看,平均时间在6000ms左右。

两张表的索引情况

o_query表的索引情况

result表的索引情况

通过执行计划的行数来看,result表应该是全表扫描了所有的记录。

猜测是不是两张表先筛选,再连接会更快呢

将SQL修改为如下

select count(1) from
              (select query_id from result oqr where oqr.create_time between '2019-09-11 00:00:01' AND '2019-10-11 23:59:59' and oqr.status = 1) oqrs
inner join (select sid from o_query oq where oq.create_time between '2019-09-11 00:00:01' AND '2019-10-11 23:59:59' and oq.type not in (2, 6)) oqs  where oqrs.query_id = oqs.sid;

查看执行计划吧

修改后的SQL执行计划

通过执行计划来看,尽管还是全表扫描,但是行数却少了很多个量级啊!
通过在平台执行这个修改后的SQL,很快就执行完了,执行时间是40ms左右,这显然已经快很多很多了!!!

为什么修改后的SQL能快这么多呢?

猜测应该是跟两张表连接查询的执行过程有关系,本人英语渣渣,从官网找到执行顺序的资料很难啊,从网上搜索资料,发现有很多网友讲的没法解释。。

掘金小册子:MySQL 是怎样运行的:从根儿上理解 MySQL

MySQL是怎样运行的:从根儿上理解MySQL

其中有一篇文章:两个表的亲密接触 —— 连接的原理
关于两表连接的执行过程,能恰好解释为什么修改后的SQL能快这么多了。。。

下图是从博文中截的图:


两表连接的执行过程

查看优化修改后的SQL

通过explain extended查看优化修改后的SQL如下:

select count(1) AS `count(1)` from `o_query` `oq` join `result` `oqr` where ((`oqr`.`status` = 1) and (`oq`.`sid` = `oqr`.`query_id`) and (`oqr`.`create_time` between '2019-09-11 00:00:01' and '2019-10-11 23:59:59') and (`oq`.`create_time` between '2019-09-11 00:00:01' and '2019-10-11 23:59:59') and (`oq`.`type` not in (2,6)))

最后的一点思考

对于这条SQL应该还有优化的空间

o_query表在 create_time 字段上也应该创建上索引

你可能感兴趣的:(把生产中的慢SQL从6000ms,一番折腾后运行只要40ms)