您当前的位置:首页 > 文章 > 一条慢 SQL 分析和优化的过程

一条慢 SQL 分析和优化的过程

作者:马文斌 时间:2023-07-13 阅读数:124 人阅读

作者分享了一条慢 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 试试。

本站大部分文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了您的权益请来信告知我们删除。邮箱:1451803763@qq.com