SQL 优化第一弹

Posted Geek_killer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL 优化第一弹相关的知识,希望对你有一定的参考价值。

由于最近公司的sql 总是总是超时,所以进行sql 的优化

原s\'q\'l :

SELECT
        temp.*,tcc.customer_level customerLevel
        FROM
        (
        SELECT
        id_car idCar,
        id_customer idCustomer,
        c.brand_id brandId,
        c.factory_id factoryId,
        c.series_id seriesId,
        c.year_id yearId,
        main_id mainId,
        NAME,
        sex,
        customer_source customerSource,
        customer_type customerType,
        company_name companyName,
        cell_phone cellPhone,
        birthday,
        car_no carNo,
        car_prefix carPrefix,
        vin,
        factory_year factoryYear,
        car_model carModel,
        car_model_short carModelShort,
        cm.id_own_org idOwnOrg,
        cm.creationtime,
        cm.creator,
        cm.modifier,
        cm.modifiedtime,
        m.points,
        m. LEVEL,
        c.car_type AS carType,
        c.car_category_id AS carCategoryId
        FROM
        tm_customer_car cm
        INNER JOIN tm_customer m ON cm.id_customer = m.pk_id
        AND m.is_del = 0
        INNER JOIN tm_car c ON cm.id_car = c.pk_id
        AND c.is_del = 0
        WHERE
        cm.id_own_org in
             ( 
                CAST(\'10545511425563138989\'  as unsigned )
             ,
                CAST(\'10545511425563140093\'  as unsigned )
             ,
                CAST(\'10545511425563140621\'  as unsigned )
             ,
                CAST(\'10545511425563141565\'  as unsigned )
             ,
                CAST(\'10545511425563139362\'  as unsigned )
             ,
                CAST(\'10545511425563136430\'  as unsigned )
             ,
                CAST(\'10545511425563138387\'  as unsigned )
             ,
                CAST(\'10545511425563137867\'  as unsigned )
             ,
                CAST(\'10545511425563139144\'  as unsigned )
             ,
                CAST(\'10545511425563139602\'  as unsigned )
             ,
                CAST(\'10545511425563128134\'  as unsigned )
             ,
                CAST(\'10545511425563140397\'  as unsigned )
             ,
                CAST(\'10545511425563137692\'  as unsigned )
             ,
                CAST(\'10545511425563140988\'  as unsigned )
             ,
                CAST(\'10545511425563138546\'  as unsigned )
             ,
                CAST(\'10545511425563139830\'  as unsigned )
             ,
                CAST(\'10545511425563136673\'  as unsigned )
             ,
                CAST(\'10545511425563138073\'  as unsigned )
             ,
                CAST(\'10545511425563137152\'  as unsigned )
             ,
                CAST(\'10545511425563138802\'  as unsigned )
             ,
                CAST(\'10545511425563137450\'  as unsigned )
             ,
                CAST(\'10545511425563135631\'  as unsigned )
             ,
                CAST(\'10545511425563136936\'  as unsigned )
             )
         and c.car_type = \'0\'
         and
            CONCAT_WS("",m.name,m.mnemonic,m.cell_Phone,c.car_prefix,c.car_no,c.contact_name,c.contact_cellphone,c.vin)
            like "%"\'28B0X\'"%"
            ORDER BY
                    cm.id_customer
                 DESC         
        Limit 0, 10
        ) AS temp
        LEFT JOIN tm_customer_config tcc ON temp. LEVEL = tcc.pk_id;

执行计划:

对表c 进行了全表查询是导致sql 的根本原因;

其实在表中已经对其中的列加了索引,但因为的最优选择而没使用,同一个sql 在本地和生产的服务器上的执行计划是不一样的,这也验证了我下面的结论;

我尝试了各种方法:

tm_customer_car   id_customer,id_own_org 都已经有索引;
给 tm_car car_type列加索引意义不大;  列的值唯一性太小 --》放弃;

而给like 里的字段加索引,因为这边用的是 ‘%like%’ , 不会使用索引–》放弃;

对 tm_customer   tm_car 选进行is_del 的select ,查询时间没怎么变化--》放弃。 

 

最总在我们架构师的提醒下发现其实只要将where 的提交提前查询就好了,这样就可以使用已经建立的索引;

 

优化后的执行计划

  INNER JOIN tm_car c ON cm.id_car = c.pk_id
        AND c.is_del = 0
        AND c.id_own_org IN (
            CAST(
                \'10545511425563138989\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563140093\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563140621\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563141565\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563139362\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563136430\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563138387\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563137867\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563139144\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563139602\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563128134\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563140397\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563137692\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563140988\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563138546\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563139830\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563136673\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563138073\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563137152\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563138802\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563137450\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563135631\' AS UNSIGNED
            ),
            CAST(
                \'10545511425563136936\' AS UNSIGNED
            )
        )
        AND c.car_type = \'0\'

 

 

总结:

1.执行计划不一致问题:

      同一个SQL语句  会由于表的数据(量、和值个数不同等)变化而执行不同的执行计划,因为随着表的数据(量、和值个数不同等)变化,执行同一个执行计划的代价也在变化。

      同一个SQL语句 选择 一个执行计划的依据:

                                表 统计信息    由此计算出  不同的执行计划的代价  由此选代价小的执行计划

 

                               表 的统计信息 一定要反映表当前的数据情况,否则选出的执行计划实际上不是代价最小那个,此时就要优化了,重新更新下表 的统计信息;

2.执行计划的解读

id
select_type
table
type
extra

sql从里向外执行,通过以上观察发现

sql是按照id从大到小执行的。

1)、SIMPLE(不使用UNION或子查询等)

2) 、PRIMARY:最外层的select

3)、DERIVED:派生表的SELECT(FROM子句的子查询)

4)、UNION:UNION中的第二个或后面的SELECT语句

5)、UNION RESULT:UNION的结果。

6)、DEPENDENT UNION:UNION中的第二个或后面的SELECT语句,取决于外面的查询

7)、SUBQUERY:子查询中的第一个SELECT

8)、DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询

有时不是真实的表名字,

看到的是derivedx(x是个数字,我的理解是第几步执行的结果)

效率最高的是system,然后依次是

const、eq_ref、ref、range、index和 All。

 一般来说,得保证查询至少达到range级别,

最好能达到ref,否则就可能会出现性能问题。

Using filesort 
看到这个的时候,查询就需要优化了。mysql需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行 
Using index 
列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候 
Using temporary 
看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上   

 

 

参考:

 

常见的优化方式:

  1. 查询条件减少使用函数,避免全表扫描
  2. 减少不必要的表连接
  3. 有些数据操作的业务逻辑可以放到应用层进行实现
  4. 可以使用with as
  5. 使用“临时表”暂存中间结果
  6. 不要把SQL语句写得太复杂
  7. 不能循环执行查询
  8. 用 exists 代替 in 
  9. 表关联关系不要太纠结
  10. 查询多用索引列取查,用charindex或者like[0-9]来代替%%
  11. inner关联的表可以先查出来,再去关联leftjoin的表
  12. 可以进行表关联数据拆分,即先查出核心数据,再通过核心数据查其他数据,这样会快得多
  13. 参考SQL执行顺序进行优化
  14. 表关联时取别名,也能提高效率
  15. 使用视图,给视图建立索引进行优化
  16. 使用数据仓库的形式,建立单独的表存储数据,根据时间戳定期更新数据。将多表关联的数据集中抽取存入一张表中,查询时单表查询,提高了查询效率。

以上是关于SQL 优化第一弹的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode SQL题目(第一弹)

mysql数据库第一弹

夜莺官方文档优化第一弹:手把手教你部署和架构讲解,消灭所有部署失败的 case!干!

Hulu机器学习问题与解答系列 | 第一弹:模型评估

JVM第一弹

JVM第一弹