作者分享了一條慢 SQL 分析和優化的過程,總結出切實有效的優化手段。
作者:馬文斌
MySQL 愛好者。
本文來源:原創投稿
- 愛可生開源社區出品,原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。
背景
開發同學丟了一條 SQL 過來。“馬哥,看看這個 SQL 能否優化,業務那邊反饋很慢!”看了下執行計劃+表結構,索引都沒問題。那到底是怎麼回事呢?咱們一起來瞧瞧。
分析原 SQL
explain SELECT
count(0)
FROM
invoice_sales_application a
WHERE
(
shop_order_id LIKE '23060919546335%'
OR (
EXISTS (
SELECT
1
FROM
invoice_sales_application_detail b
WHERE
a.application_no = b.application_no
AND a.invoice_category = b.invoice_category
AND b.del_flag = 0
AND b.shop_order_id LIKE '23060919546335%'
)
AND a.is_merge = 1
)
)
先來看看這個 SQL 是什麼意思:
在 invoice_sales_application
表中,shop_order_id
以 '23060919546335%'
開頭,或者存在一個相關的 invoice_sales_application_detail
表中的記錄,該記錄的 application_no
和 invoice_category
與 invoice_sales_application
表中的相同,並且 shop_order_id
以 '23060919546335%'
開頭,同時 invoice_sales_application
表中的 is_merge
字段爲 1
。
執行計劃:all+ref,其中 a
表要掃描 116w 行的數據。
執行需要 43s,且有一個全表掃描。
優化操作
EXISTS 轉化成 JOIN 的方式
這裏是把 EXISTS
改寫成 INNER JOIN
通過索引關鍵關聯,應該會有不錯的效果,試試看。
SELECT count(0)
FROM invoice_sales_application a INNER
JOIN invoice_sales_application_detail b
ON a.application_no = b.application_no
WHERE ( a.shop_order_id LIKE '23060919546335%'
OR ( b.shop_order_id LIKE '23060919546335%'
AND a.is_merge = 1 ) )
AND a.invoice_category = b.invoice_category
AND b.del_flag = 0
這裏雖然轉化了 INNER JOIN
的方式,執行計劃還是 all+ref ,因爲用了 OR
導致 a
表沒有用上索引,還是用的全表掃描。沒關係,咱們再次進行轉化。
OR 改成 UNION
SELECT count(*)
FROM invoice_sales_application a
INNER JOIN invoice_sales_application_detail b ON a.application_no = b.application_no
AND a.invoice_category = b.invoice_category
AND b.del_flag = 0
WHERE a.shop_order_id = '23060919546335'
AND a.del_flag = 0
UNION
SELECT count(*)
FROM invoice_sales_application a
INNER JOIN invoice_sales_application_detail b ON a.application_no = b.application_no
AND a.invoice_category = b.invoice_category
AND b.del_flag = 0
WHERE b.shop_order_id = '23060919546335'
AND a.is_merge = 1
AND a.del_flag = 0;
在看看執行計劃,eq_ref+ref+ref+ref。說明已經優化的很好了,起碼沒有全表掃描。
最後看看結果。
這樣 SQL 執行很快了,查詢時間從 42s 降到 18ms,快了幾個數量級。
小結
1、當 SQL 的主架構中含有 EXISTS
的時候,可以改成 INNER JOIN
的方式,先看看效果。
2、當條件中有 OR
的時候,可以改成 UNION
試試。
活動推薦
關於 SQLE
愛可生開源社區的 SQLE 是一款面向數據庫使用者和管理者,支持多場景審覈,支持標準化上線流程,原生支持 MySQL 審覈且數據庫類型可擴展的 SQL 審覈工具。