使用explain分析sql語句————記一次優化
直接上sql,省略必要條件,在測試環境分析,資料較少,但依舊可看出問題:
zl_transport_order 運單表,可理解為訂單表,id為運單id,transport_order_no為運單號
zl_package 包裹表,transport_order_id為運單id,transport_order_no為運單號
EXPLAIN SELECT
t1.*
FROM
zl_transport_order AS t1
JOIN zl_package AS t2 ON t1.id = t2.transport_order_id
JOIN zl_user_address_snapshot t3 ON t1.recipient_address_id = t3.id
WHERE
t1.active = 1
AND t1.state IN (0, 20, 40, 10, 30)
GROUP BY
t1.transport_order_no
ORDER BY
t1.create_date DESC
這條sql比較有問題的是用了臨時表和join buffer(使用BNL演算法)
簡單解釋下Extra的幾個引數 :
Using where:優化器需要通過索引回表查詢資料,除主鍵索引外,其餘索引只存索引欄位與主鍵,一般查詢欄位不在索引裡,就要回表,是正常的。
Using index:直接訪問索引就可獲取到所需資料,不需要回表(回到主鍵索引查詢),代表查詢欄位只有索引欄位和主鍵,只走一遍索引樹即可,查詢很快。
Using filesort :當Query中包含 ORDER BY 操作,而且無法利用索引完成排序操作的時,MySQL Query Optimizer不得不選擇相應的排序演算法來實現,資料較少時從記憶體排序,否則從磁碟排序,主要看要排序欄位的資料量,如果很大,建議建個索引,直接走索引排序。
Using join buffer (Block Nested Loop):將外層迴圈的行/結果集存入join buffer, 內層迴圈的每一行與整個buffer中的記錄做比較,從而減少內層迴圈的次數,多次掃描被驅動表,佔用磁碟 IO 資源,大表會佔用非常多CPU 資源,可能導致 Buffer Pool 的熱資料被淘汰,影響記憶體命中率,總之不要用。一般是被驅動表用不上索引,才使用BNL 演算法,可考慮建個索引解決。
先來解決Using join buffer (Block Nested Loop)的問題:被驅動表zl_package實際是有索引的,只是在欄位transport_order_no上,而zl_transport_order表上欄位transport_order_no也有索引 ,所以此處只是將表接連條件換成一個有索引 的欄位:
EXPLAIN SELECT
t1.*
FROM
zl_transport_order AS t1
JOIN zl_package AS t2 ON t1.transport_order_no= t2.transport_order_no
JOIN zl_user_address_snapshot t3 ON t1.recipient_address_id = t3.id
WHERE
t1.active = 1
AND t1.state IN (0, 20, 40, 10, 30)
GROUP BY
t1.transport_order_no
ORDER BY
t1.create_date DESC
效果:
最後來解決臨時表的問題:
EXPLAIN SELECT
t1.*
FROM
zl_transport_order AS t1
JOIN zl_package AS t2 ON t1.transport_order_no= t2.transport_order_no
JOIN zl_user_address_snapshot t3 ON t1.recipient_address_id = t3.id
WHERE
t1.active = 1
AND t1.state IN (0, 20, 40, 10, 30)
GROUP BY
t1.id
ORDER BY
t1.id DESC
將GROUP BY與ORDER BY 後的欄位換成id ,效果跟之前是一樣的,但卻可以利用主鍵索引來排序,不需要再利用額外的空間排序
效果:
總之,使用explain,主要有三個關注點:
首先是extra,可能有些屬性不太常用,可以百度看看,思考下解決方案,嘗試多改改;
然後是key,看下有沒有用上索引,用上了哪個索引,如果建了索引但沒用上,思考下是什麼原因;
最後就是row,掃描行數,這個參考來看,如果有很多頁空洞可能會導致查詢不準。