mysql慢sql优化实战:in order by优化处理

背景

     线上查询慢的问题日益突出,专门写一个帖子记录一下处理过程,会定期更新优化处理方案
     套餐余量统计查询菜单,数据库查询时间一分钟五十秒,优化之后耗时109毫秒,性能提升很大.所有时间统计均以数据库层面进行统计.用户使用层面因为有数据传输、带宽、业务逻辑处理等因素查询时间会更长,只看数据库查询层面可以排除其他因素影响。
     待优化sql:

SELECT manage_staff_card.id,manage_staff_card.card_no, manage_staff_card.status, manage_staff_card.rest_count,
        manage_staff_card.card_type, manage_card.time_type , manage_staff_card.single_price
        FROM manage_staff_card 
        LEFT JOIN `manage_staff` ON manage_staff.`login` = manage_staff_card.`login` LEFT JOIN `manage_card`
        ON manage_card.`id` = manage_staff_card.`card_id` LEFT JOIN `manage_studio` ON manage_studio.`id` = manage_staff_card.`studio_id`
        LEFT JOIN `manage_staff_studio` ON manage_staff_studio.`login` = manage_staff_card.`login` AND manage_staff_studio.`studio_id` = manage_staff_card.`studio_id`
        LEFT JOIN `manage_user_studio` ON manage_user_studio.`login` = manage_staff_studio.`teacher_login` AND manage_staff_studio.`studio_id` = manage_user_studio.`studio_id`
        LEFT JOIN `manage_contract_card` ON manage_contract_card.`staff_card_id` = manage_staff_card.`id` 
        LEFT JOIN `manage_contract` ON manage_contract.`contract_no` = manage_contract_card.`contract_no`
        WHERE manage_staff_card.status IN(8,5,15,20) ORDER BY manage_staff_card.create_time DESC

    执行计划如下:
在这里插入图片描述
    根据优化经验,一般type为all、index都需要进行优化,最少保证到ref。先从下面开始看
    manage_contract中type为all,看下sql中manage_contract用作表连接,直接将contract_no字段添加索引:

ALTER  TABLE  manage_contract  ADD  INDEX  idx_card_no (contract_no);

    添加成功后查询索引:

SHOW INDEX FROM manage_contract

    重新explain:
在这里插入图片描述
    type变更为ref,预估扫描的行数变更为1.继续看往下看.
    manage_contract_card中type为all,看sql中manage_contract_card中staff_card_id、contract_no在查询中都体现,是不是两个都需要创建索引呢,答案是不需要,只需要对staff_card_id进行创建索引。自己测试过两个都添加索引最终的执行计划type会变更为index,index和all都算是全表扫描,因为添加上contract_no之后再和manage_contract联查时需要按照contract_no全部扫描一遍manage_contract_card,实际上执行上一步的时候已经根据staff_card_id将符合要求的manage_contract_card查询出来,所有这里对于manage_contract_card只添加一个staff_card_id即可。
    添加索引

ALTER  TABLE  manage_contract_card  ADD  INDEX  idx_staff_card_id (staff_card_id);

    添加成功后查询索引:

SHOW INDEX FROM manage_contract_card  

    重新explain:
在这里插入图片描述
    type变更为ref,预估扫描的行数变更为1.继续看往下看.
    manage_staff_card放到最后讲是因为这个会比较特殊。首先看排序,排序会导致索引失效,对按照创建时间排序的业务来讲,可以使用主键id进行倒序可以进行替换实现业务逻辑,另外不需要单独根据创建时间字段添加索引,直接使用主键索引。毕竟不是索引越多越好。修改sql之后重新explain:
在这里插入图片描述
    type变更为index,由于是id主键排序,所以基本上开始全表扫描.下面在看manage_staff_card中status.直接给status添加索引:

ALTER  TABLE  manage_staff_card  ADD  INDEX  idx_status (STATUS); 

    重新执行explain发现索引是不生效的,是因为in会导致索引失效,继续往下看.
在这里插入图片描述

    关于in的业务场景实际开发中还是比较多的,i查阅资料总结了一下in常用的优化方案:

1.使用JOIN替代IN查询。如果条件中的IN查询可以转换为JOIN查询,有可能会对性能有一定提升
2.IN查询中的值列表存储到临时表中,然后使用JOIN查询
3.exists能否替代
注意:对于in中全部是参数值的,使用exist代替in无效

    以上各种优化方案都试过,对于本场景中in为参数值的情况,上面都不适用,这里的处理方式是对于manage_staff_card 中的status索引进行强制执行,manage_staff_card后面添加FORCE INDEX(idx_status).重新执行explain:
在这里插入图片描述
    会发现type类型变更为range.重新执行sql后耗时:0.109秒
    最终优化后的sql:

EXPLAIN  SELECT manage_staff_card.id,manage_staff_card.card_no, manage_staff_card.status, manage_staff_card.rest_count,
        manage_staff_card.card_type, manage_staff.real_name ,  manage_card.time_type , manage_staff_card.single_price
        FROM manage_staff_card  FORCE INDEX(idx_status)
        LEFT JOIN `manage_staff` ON manage_staff.`login` = manage_staff_card.`login` LEFT JOIN `manage_card`
        ON manage_card.`id` = manage_staff_card.`card_id` LEFT JOIN `manage_studio` ON manage_studio.`id` = manage_staff_card.`studio_id`
        LEFT JOIN `manage_staff_studio` ON manage_staff_studio.`login` = manage_staff_card.`login` AND manage_staff_studio.`studio_id` = manage_staff_card.`studio_id`
        LEFT JOIN `manage_user_studio` ON manage_user_studio.`login` = manage_staff_studio.`teacher_login` AND manage_staff_studio.`studio_id` = manage_user_studio.`studio_id`
        LEFT JOIN `manage_contract_card` ON manage_contract_card.`staff_card_id` = manage_staff_card.`id` 
        LEFT JOIN `manage_contract` ON manage_contract.`contract_no` = manage_contract_card.`contract_no`
        WHERE manage_staff_card.status IN(8,5,15,20) ORDER BY manage_staff_card.id DESC

    总结:
    1.left join中添加索引一般是从left join后面的表中的连接字段进行添加索引,而不是只要left join表的查询条件中有的字段都添加索引.
    2.order by排序时可以使用主键排序替换字符串排序;
    3.in的常用优化处理方式,上面已补充,这里不再重复,对于in中参数值的情况创建索引之后需要添加强制索引;
    以上sql优化的处理过程,如果看到这里感觉有所帮助欢迎点赞或收藏!下面是最近参与的一个匿名社交类的微信小程序,有兴趣的可以看下
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43401380/article/details/131061768