一次mysql多表查詢(left jion)優化案例
在新上線的供需模塊中,發現某一個查詢按鈕點擊后,出不來結果,找到該按鈕對應sql手動執行,發現需要20-30秒才能出結果,所以服務端程序判斷超時,故先不顯示結果
以下是對這條查詢的優化記錄
1 數據庫配置
數據庫配置:4C8G
主表數據:3W+
2 sql語句
提取sql語句,簡化如下
SELECT
taba.id,
taba.title,
taba.type,
taba.end_time,
tabb.username,
tabc.orgname
FROM
taba
LEFT JOIN tabd ON tabd.info_id = taba.id
LEFT JOIN tabe ON tabe.sdo_id = taba.id
LEFT JOIN tabb ON tabb.id = taba.creator
LEFT JOIN tabc ON tabc.id = taba.organization_id
WHERE
taba.`STATUS` = 'PUBLISH'
AND tabd.type = 'INDEX'
AND tabd.`VALUE` = '1'
AND taba.info_type = 'SUPPLY'
GROUP BY
taba.id
ORDER BY
taba.create_time DESC
LIMIT 100
3 優化記錄
3.1 數據庫索引
首先第一反應,查sql是否走了索引
EXPLAIN
select ......
從索引檢查結果發現
tabe
只有主鍵索引,沒有sdo_id
的索引
經過確定sdo_id
是通過uuid制作,重復數很少,可以增加上索引- 其他條件雖然也沒走索引,但是屬於枚舉值,重復性高,沒有加索引的條件
經過添加索引,數據查詢時間降低到3秒以內,所以正確的索引才是王道
3.2 返回數據限制
經過與開發人員溝通,確定可以每次只取10條數據,所以要求他們更改limit語句限制為limit 10
經過修改limit語句,數據庫直插時間已經變味1.7秒
3.3 spring框架錯誤的conut *
經過前兩部優化,按理2秒左右app就能顯示數據,但是時間上卻需要4秒鍾
通過sql慢查詢日志,發現在這條sql執行前,spring框架自動執行了一個select count(0) from (......)
的操作來做分頁,但是所以導致查詢時間是理論上的2倍
再次與開發確定,不用框架的自動分頁功能,改為代碼層手動分頁
結果修改框架的分頁,app查詢時間達到2秒內,已基本得到解決
3.4 極限優化limit
查詢優化到2秒,已基本可以接受,但是先到數據才3萬多行,感覺還是不能接受,繼續找原因,發現如下:
- 3萬+數據,但是sql執行結果卻顯示掃描了100多萬行數據
觀察sql語句,可以發現是先做了多次left join
后,對結果取limit
,那能不能先取limit 10
再進行查詢呢,於是把sql優化如下
SELECT
taba.id,
taba.title,
taba.type,
taba.end_time,
tabb.username,
tabc.orgname
FROM
taba
LEFT JOIN tabd ON tabd.info_id = taba.id
LEFT JOIN tabe ON tabe.sdo_id = taba.id
LEFT JOIN tabb ON tabb.id = taba.creator
LEFT JOIN tabc ON tabc.id = taba.organization_id
WHERE
tabd.type = 'INDEX'
AND tabd.`VALUE` = '1'
AND taba.id IN (
SELECT * FROM
( SELECT id FROM taba WHERE `STATUS` = 'PUBLISH'
AND info_type = 'SUPPLY'
ORDER BY taba.create_time DESC LIMIT 10 ) AS tmp
)
優化方法:
- 將limit語句通過子查詢放入where條件中
- sql將先執行子查詢獲取10條
id
數據 - 讓后將10條
id
拿去前面做join
優化結果
- sql執行時間達到0.117秒,再一次質的飛躍
- 基本做到秒加載,點擊按鈕,一秒內出結果
3.4.1
第二天一覺醒來,覺得這個sql還有值得優化的地方,於是分別提取出各語句執行后,發現耗時最長的是limit條件,耗時0.09s
優化方法
- 查找發現原因是
order by
條件create_time
列未加索引,導致做了一次全表掃描 - 於是增加上
create_time
索引
優化結果
- sql執行時間變為0.068s
- 再次說明正確的索引才是王道
3.5 優化后記
其實sql中還有幾個可以優化的地方,比如:
- 4個
left join
中的3個可以改成inner join
- 原語句的
group by
,經測試改掉可優化0.3秒(1.7秒處) - limit語句可以放到from處先處理等
但是:
- sql優化是長期的過程
- 優先解決影響業務的慢查詢
- 優先解決占時間比例大的慢查詢
- 咱是運維,不是DBA,還有高可用等着我玩
- 因此已經達到要求,甚至超額完成,就不用再管芝麻了