乐趣区

关于mysql:又是一条慢-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_audit…
退出移动版