又是一條慢 SQL 改寫,拿捏!

作者分享了一條慢 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_noinvoice_categoryinvoice_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 審覈工具。

SQLE 獲取

類型 地址
版本庫 https://github.com/actiontech/sqle
文檔 https://actiontech.github.io/sqle-docs/
發佈信息 https://github.com/actiontech/sqle/releases
數據審覈插件開發文檔 https://actiontech.github.io/sqle-docs-cn/3.modules/3.7_auditplugin/auditplugin_development.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章