問題SQL優化:從超過300s優化到1s案例分析
概述
今天發現有個項目應用每次一啟動后使用就會導致另外一個應用服務直接崩潰,而這兩個應用使用的是同個數據庫,經過排查可以發現是報表應用的某個查詢功能導致,而咨詢開發但卻查不出是哪條sql,那就只能靠自己排查了..下面是解決的大致過程。
1、開啟慢查詢
修改my.ini配置,增加參數
- slow-query-log=1
- slow_query_log_file="epms-slow.log"
- long_query_time=10
修改后重啟,觀察epms-slow.log日志內容。
2、定位慢sql
重新點擊報表模塊,選擇日期后點擊查詢,等系統崩潰后,觀察日志涉及的慢查詢sql
發現問題sql如下:
- select id, parent, project, name
- from zentao.zt_task
- where parent = 0
- /*and exists (select t.parent from zentao.zt_task t where t.parent > 0)*/
- and id in (
- select t.parent from zentao.zt_task t where t.parent > 0
- );
3、查看執行計劃
- explain select id, parent, project, name
- from zentao.zt_task
- where parent = 0
- /*and exists (select t.parent from zentao.zt_task t where t.parent > 0)*/
- and id in (
- select t.parent from zentao.zt_task t where t.parent > 0
- );
這里可以看到因為走的全掃,每次都掃5萬條,產生笛卡爾積,5萬*5萬就導致數據庫崩潰了。
4、考慮用exists改寫sql
- explain select id, parent, project, name
- from zentao.zt_task t
- where parent = 0 and exists (
- select a.parent from zentao.zt_task a where a.parent = t.id
- )
這里改寫后問題還是沒解決。
5、考慮with改寫
后來發現zt_task表查詢了兩次,所以考慮with改寫來簡化,只查一次
- WITH tmp AS ( SELECT * FROM zt_task ) SELECT
- *
- FROM
- tmp t1
- JOIN tmp t2 ON t1.id = t2.parent
好吧,mysql5.7還不支持with改寫,只有到mysql 8版本才支持,所以這里只能放棄這種辦法了
6、用子查詢join改寫
- SELECT
- distinct t1.parent,
- t1.id,
- t1.project,
- t1.NAME
- FROM
- zentao.zt_task t1
- JOIN ( SELECT t.parent FROM zentao.zt_task t WHERE t.parent > 0 ) ta ON t1.id = ta.parent
- AND t1.parent =0
這里要記得去重,改寫后查詢在1秒內得出結果,滿足需求。
總結
通過這道案例一定要記住,多表查詢的性能是很差的,當然,性能差是有一個前提的:數據量大。子查詢 = 簡單查詢 + 限定查詢 + 多表查詢 + 統計查詢的綜合體;
在之前強調過多表查詢不建議大家使用,因為性能很差,但是多表查詢最有利的替代者就是子查詢,所以子查詢(子查詢指的就是在一個查詢之中嵌套了其他的若干查詢)在實際的工作之中使用的相當的多。