zoukankan      html  css  js  c++  java
  • MySQL的SQL优化4例

      说明:

         最近优化慢SQL,执行计划错误和OR条件查询优化实战经验,提供优化SQL思路和方法:

          1,利用exists来优化SQL(利用exists减少回表查询次数和确定驱动表)

          2,OR语句优化(OR条件,字段有索引,无法使用索引的) 

    案例1:

    SELECT sum( CASE WHEN ols.check_status NOT IN ( 234 ) THEN 1 WHEN ols.check_status IS NULL THEN 1 END ) AS lesson_num, sum( ols.check_status = 1 ) AS attend_num FROM  ol_live_student_time_chapter_list olstcl LEFT JOIN ol_live_student ols ON olstcl.live_student_id = ols.id inner JOIN ol_user u ON u.id = olstcl.user_id
    WHERE  olstcl.class_course_type = '2'  AND olstcl.attend_status <> 4
     AND olstcl.start_time >= 1621526400  AND olstcl.start_time < 1621612799   AND u.is_test_user = '0'  AND counselor_id = '796'
     AND u.pay_status = '0'  AND u.type = '1'  AND u.STATUS = '1';

      执行超过2.2秒,执行计划如下,从执行计划看:返回的rows也很少。key里用到了索引,按理说这个SQL是最优的SQL,没有优化的空间

     

     但实际查看ol_user表,表有430万条数据,核心还是查询ol_user表的数据,如果能去掉ol_user表查询,就更好,如果不能去掉,能有其他优化方法

      通过仔细分析3个表,各个条件查询查出的数据:     

    select count(*) ol_live_student_time_chapter_list olstcl where olstcl.start_time>= 1621526400 AND olstcl.start_time< 1621612799 and  olstcl.class_course_type = '2'  AND olstcl.attend_status <> 4

     发现ol_live_student_time_chapter_list 查询,这个时间查出只有8000多条。而查询oL_user表查询:  

    select count(*) from ol_user u where u.is_test_user = '0' AND counselor_id = '796' AND u.pay_status = '0' AND u.type = '1' AND u.STATUS = '1'

      查出300多条,但仔细计划显示:Using intersect(pay_status_user,counselor_id,type,is_test_user); Using where; Using index。 的确用很多索引,

      这样看,表面看执行计划没问题,但实际看一下,这个ol_user表查询,其实只是一个条件,ol_user没有字段在select查出显示,从某种意义上讲,只需要查出符合条件的就可以,如果满足其中

    一个就返回效率是否更高,不需要每个条件都查完,这样我们完全可以用EXISTS 替换inner join 来提高查询效率,SQL修改如下:

    SELECT sum( CASE WHEN ols.check_status NOT IN ( 234 ) THEN 1 WHEN ols.check_status IS NULL THEN 1 END ) AS lesson_num,
     sum( ols.check_status = 1 ) AS attend_num FROM  ol_live_student_time_chapter_list olstcl  LEFT JOIN ol_live_student ols ON olstcl.live_student_id = ols.id
    WHERE olstcl.class_course_type = '2' AND olstcl.attend_status <> 4  AND olstcl.start_time >= 1621526400
     AND olstcl.start_time < 1621612799  and EXISTS (select 1 from ol_user u  where u.is_test_user = '0'  AND u.counselor_id = '796'  AND u.pay_status = '0'  A
    ND u.type = '1' AND u.STATUS = '1' and u.id = olstcl.user_id) ; 

      使用优化的SQL,时间只有0.9秒左右

      优化原理:  利用 EXISTS 来替换 inner join,减少查询循环回表次数,提高效率。

    案例2:

    select count(distinct u.id) from ol_user u left join ol_user_related_info uri on u.id = uri.user_id where u.type=1 and u.status =1
    and u.pay_status=0 and uri.recovery_time_no_pay>=1622649600 and uri.recovery_time_no_pay<1622736000 and u.counselor_id in  (403); 

    执行超过1.3秒,执行计划如下:

     

      从这里看:查出ol_user的u.id,distinct汇总,而ol_user_related_info 是left join,仔细看逻辑,虽然是left join,但有uri.recovery_time_no_pay条件,就这个left join 可以改成inner join

     可以改成: 

    select count(distinct uri.user_id) from ol_user u inner join ol_user_related_info uri on u.id = uri.user_id where u.type=1 and u.status =1
    and u.pay_status=0 and uri.recovery_time_no_pay>=1622649600 and uri.recovery_time_no_pay<1622736000 and u.counselor_id in  (403); 

    修改完,2者的执行效率,查不多,改了也没优化,如上面的优化,我们知道ol_user表的数据量太大,要减少回表的查询,这样的sql就可以改成exists查询,如下:

    select count(distinct uri.user_id) from  ol_user_related_info uri   where   uri.recovery_time_no_pay>=1622649600 and uri.recovery_time_no_pay<1622736000 
    and exists (select 1 from ol_user u where u.id = uri.user_id and u.type=1 and u.status =1 and u.pay_status=0 and u.counselor_id in  (403) )

     改成这样,SQL执行只需0.6秒左右,未加索引,2次修改SQL后,就优化了SQL

    案例3

    SELECT f.flow_type,count(1) as num FROM `ol_admin_flow` `f` LEFT JOIN `ol_admin_flow_node` `n` ON `f`.`flowid`=`n`.`flowid` WHERE( `n`.`adminid` = 7417 OR `f`.`post_adminid` = 7417 ) GROUP BY `f`.`flow_type`;

      执行超过2.5秒,post_adminid加索引,SQL也用不到索引,将or改写成 union all写法

    select flow_type,sum(num) num from (SELECT f.flow_type,1 num FROM ol_admin_flow f LEFT JOIN ol_admin_flow_node n ON f.flowid=n.flowid
    WHERE f.post_adminid = 7418 union all SELECT f.flow_type,1 num FROM ol_admin_flow f LEFT JOIN ol_admin_flow_node n ON f.flowid=n.flowid WHERE n.adminid = 7418 ) a group by flow_type

    改成这样写法后。SQL效率大幅提升,查询只需0.3秒

    案例4

    SELECT
        au.GROUP group_id,sum(IF
        (    o.STATUS = 3     AND o.attach_pay_time BETWEEN 1625068800     AND 1627747199 AND o.rebate_time BETWEEN 1625068800     AND 1627747199,    0,
        oap.price     )     ) real_renewal_money,
        sum(IF    (    o.STATUS = 3     AND o.attach_pay_time BETWEEN 1625068800     AND 1627747199 
        AND o.rebate_time BETWEEN 1625068800     AND 1627747199,    0,    oap.order_count     )     ) renewal_num 
    FROM
        `ol_order_attach_pay` `oap`    INNER JOIN `ol_order` `o` 
        IGNORE INDEX ( idx_pay_time_type ) ON `o`.`order_number` = `oap`.`order_number` 
        AND `o`.`status` IN ( 1, 3 )     AND ( `o`.`attach_pay_time` >= 1625068800 AND `o`.`attach_pay_time` < 1627055999 ) 
        AND `o`.`package_course_type` = 1    INNER JOIN `online`.`ol_admin_user` `au` ON `oap`.`adminid` = `au`.`id` 
    WHERE
        `oap`.`status` = 1     AND `oap`.`adminid` IN (
        728,    818,    870,    1497,    2019,    2021,
        2465,    2557,    2679,    3228,    3231,    3916,    3419,    3423,    3412,
        3417,    3500,    4165,    477,    4163,    1030,    562
        ) 
    GROUP BY    `au`.`group`     LIMIT 100;
    View Code

       该SQL线上执行需要18秒以上,执行计划如下:

      从执行计划看,慢的地方在查询ol_order的索引,有76万条。表中有以下索引:

        KEY `attach_pay_time` (`attach_pay_time`)    KEY `package_course_type` (`package_course_type`),表中有20多个索引,太多,真不想再建索引。但不加索引无法优化,用

     force index 索引优化效果又不太好,一开始研发建立复合索引:KEY `idx_pay_time_type` (`attach_pay_time`,`package_course_type`), 按理应该优化,但实际优化效果不好,后续将复合索引改成:

    KEY `idx_pay_time_type` (`package_course_type`,`attach_pay_time`),

       加好后,执行计划如下,查询在0.5秒:

       

    总结:

        1,优化SQL,MySQL需要选择正确的驱动表,如果执行计划不正确,可用exists来明确驱动表

        2,加复合索引,最好先测试执行效果,有时预想和实际不一样
  • 相关阅读:
    Linux IO模型漫谈(3) 阻塞式IO实现
    Linux IO模型漫谈(4) 非阻塞IO
    Linux IO模型漫谈(6) 信号驱动IO模型
    Go语言_反射篇
    Linux IO模型漫谈(5) IO复用模型之select
    Go语言_函数学习篇
    Go语言_接口篇
    nginx源码学习Unix Unix域协议
    Java GC
    Heritrix 3.1.0 源码解析(三十四)
  • 原文地址:https://www.cnblogs.com/zping/p/14861810.html
Copyright © 2011-2022 走看看