慢SQL報警
郵件接收到了慢SQL報警,一看時間嚇一跳,竟然將近26s,看看是什么妖魔鬼怪?
由于涉及到生產(chǎn),表名經(jīng)過處理,并不是真實表名。。。
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"]
開始分析
執(zhí)行計劃
初看就是兩個表內(nèi)連接,那看一看執(zhí)行計劃吧。。。
平臺輸出執(zhí)行計劃格式有問題,湊合看吧。
原始SQL執(zhí)行計劃
有一個全表掃描,而且行數(shù)很多啊,難怪很慢!
生產(chǎn)環(huán)境通過頁面跳板的方式查詢數(shù)據(jù)庫,將這條SQL在頁面執(zhí)行的時候,第一次沒有查出結(jié)果,因為平臺有執(zhí)行時間限制(20s),也意味著這條SQL超過20s還沒有執(zhí)行處理結(jié)果。
后來又執(zhí)行了幾次,通過執(zhí)行記錄來看,平均時間在6000ms左右。
兩張表的索引情況
o_query表的索引情況
result表的索引情況
通過執(zhí)行計劃的行數(shù)來看,result表應(yīng)該是全表掃描了所有的記錄。
猜測是不是兩張表先篩選,再連接會更快呢
將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;
查看執(zhí)行計劃吧
修改后的SQL執(zhí)行計劃
通過執(zhí)行計劃來看,盡管還是全表掃描,但是行數(shù)卻少了很多個量級啊!
通過在平臺執(zhí)行這個修改后的SQL,很快就執(zhí)行完了,執(zhí)行時間是40ms左右,這顯然已經(jīng)快很多很多了?。?!
為什么修改后的SQL能快這么多呢?
猜測應(yīng)該是跟兩張表連接查詢的執(zhí)行過程有關(guān)系,本人英語渣渣,從官網(wǎng)找到執(zhí)行順序的資料很難啊,從網(wǎng)上搜索資料,發(fā)現(xiàn)有很多網(wǎng)友講的沒法解釋。。
掘金小冊子:MySQL 是怎樣運行的:從根兒上理解 MySQL
MySQL是怎樣運行的:從根兒上理解MySQL
其中有一篇文章:兩個表的親密接觸 —— 連接的原理
關(guān)于兩表連接的執(zhí)行過程,能恰好解釋為什么修改后的SQL能快這么多了。。。
下圖是從博文中截的圖:
兩表連接的執(zhí)行過程
查看優(yōu)化修改后的SQL
通過explain extended查看優(yōu)化修改后的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應(yīng)該還有優(yōu)化的空間
o_query表在 create_time 字段上也應(yīng)該創(chuàng)建上索引