记一次MYSQL的查询优化(主要集中在建立索引消除USING filesort )

本文详述了一次对复杂MYSQL查询的优化过程,通过建立索引、子查询优化和调整JOIN顺序,将原本耗时较长的查询(50秒)优化至0.01秒。关键步骤包括将全表查询改为子查询,使用延迟关联优化和通过索引消除USING filesort。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

回顾一下最近做的一个查询优化 ,该sql是一个页面查询,历史遗留问题就不多说。。当我接手的时候(去掉搜索条件)的sql为

MYSQL 5.7.23

original版本

select a.*, m.fitment_contract_amount
        from (select t.id,
            t.task_no,
            t.apply_time,
            t.urgency_level,
            t.product_id,
            t.operator_id auditUserId,
            b.customer_name,
            b.customer_id_no,
            b.customer_phone,
            ifnull(f.merchant_code,cmi.merchant_code) merchant_code,
            ifnull(f.merchant_name,cmi.merchant_name) merchant_name,
            ifnull(f.store_code,cmi.store_code) store_code,
            ifnull(f.store_name,cmi.store_name) store_name,
            a.apply_amount,
            a.pre_amount,
            s.loan_amount,
            s.term,
            s.loan_time,
            (select result from customer_audit_phone_result where task_no=t.task_no and delete_flag=0 order by id desc limit 1) phoneResult,
            
            (select id from customer_audit_result where task_no = a.task_no order by id desc limit 1) audit_result_id
        from customer_audit_task t
        left join customer_base_info b on t.task_no = b.task_no and b.delete_flag=0
        left join customer_fitment_info f on t.task_no = f.task_no and f.delete_flag=0
        left join customer_apply_info a on t.task_no = a.task_no and a.delete_flag=0
        left join customer_loan_success_info s on t.task_no = s.task_no and s.delete_flag=0
        left join customer_merchant_info cmi on t.task_no=cmi.task_no and cmi.delete_flag=0
        where t.delete_flag=0) a
        left join customer_material_info m on m.customer_audit_result_id = a.audit_result_id and m.delete_flag=0
        where find_in_set(a.product_id,"1,6")
        order by a.apply_time desc
        limit 0,10 

可以看出该sql原来的写法是将customer_audit_task 的全表查询出 然后再排序分页(耗时很长,页面接口请求大约5万条记录需要60S),第一步先想到将子查询去掉,省去全表查出的过程,同时关注到audit_result_id的逻辑是取一条,将其通过group by的方式获取

VERSION 1.0

 

select   t.id,
            t.task_no,
            t.apply_time,
            t.urgency_level,
            t.product_id,
            t.operator_id auditUserId,
            b.customer_name,
            b.customer_id_no,
            b.customer_phone,
            ifnull(f.merchant_code,cmi.merchant_code) merchant_code,
            ifnull(f.merchant_name,cmi.merchant_name) merchant_name,
            ifnull(f.store_code,cmi.store_code) store_code,
            ifnull(f.store_name,cmi.store_name) store_name,
            a.apply_amount,
            a.pre_amount,
            s.loan_amount,
            s.term,
            s.loan_time,
            (select result from customer_audit_phone_result where task_no=t.task_no and delete_flag=0 order by id desc limit 1) phoneResult,
            
            car.id audit_result_id,
        m.fitment_contract_amount
        from customer_audit_task t
        left join customer_base_info b on t.task_no = b.task_no and b.delete_flag=0
        left join customer_fitment_info f on t.task_no = f.task_no and f.delete_flag=0
        left join customer_apply_info a on t.task_no = a.task_no and a.delete_flag=0
        left join customer_loan_success_info s on t.task_no = s.task_no and s.delete_flag=0
        left join customer_merchant_info cmi on t.task_no=cmi.task_no and cmi.delete_flag=0
        left join(select task_no, max(id) id  from customer_audit_result where delete_flag = 0 group by task_no )car on car.task_no = t.task_no 
        left join customer_material_info m on m.customer_audit_result_id = car.id and m.delete_flag=0
        where find_in_set(t.product_id,"1,6") and t.delete_flag=0
         order by t.apply_time desc
        limit 0,10 

结果虽然有所改善,但是依然很慢(10S左右),可以看到主要还是查询customer_audit_task表,在关联查询中可以通过延迟关联进行优化

举个例子

正常写法

select id, apply_code, request_str from manage_inner_api_log order by id desc limit 97000,10;

延迟关联优化(通过子查询覆盖索引)

select id, apply_code, request_str from manage_inner_api_log INNER JOIN (select id from manage_inner_api_log order by id desc limit 97000, 10)as limt USING(id);

即在INNER JOIN中通过覆盖索引将符合条件的主键筛选出提高查询效率

VERSION 2.0

 select t.id,
             t.task_no,
            t.apply_time,
          t.urgency_level,
            t.product_id,
        t.operator_id auditUserId,
            b.customer_name,
            b.customer_id_no,
            b.customer_phone,
            ifnull(f.merchant_code,cmi.merchant_code) merchant_code,
            ifnull(f.merchant_name,cmi.merchant_name) merchant_name,
            ifnull(f.store_code,cmi.store_code) store_code,
            ifnull(f.store_name,cmi.store_name) store_name,
            a.apply_amount,
            a.pre_amount,
            s.loan_amount,
            s.term,
            s.loan_time,
            (select result from customer_audit_phone_result where task_no=t.task_no and delete_flag=0 order by id desc limit 1) phoneResult,
            t.real_status audit_status,
            car.id audit_result_id,
        m.fitment_contract_amount
        from customer_audit_task t 
        inner join (select id from customer_audit_task
                where 
                    find_in_set(product_id,"1,6") 

 and audit_status not in(100, 103, 1001, 1002) 
                    and delete_flag=0 
                  
                        

                    order by apply_time desc limit 0,10
        )a on t.id = a.id 

        left join customer_base_info b on t.task_no = b.task_no and b.delete_flag=0
        left join customer_fitment_info f on t.task_no = f.task_no and f.delete_flag=0
        left join customer_apply_info a on t.task_no = a.task_no and a.delete_flag=0

        left join customer_loan_success_info s on t.task_no = s.task_no and s.delete_flag=0
        left join customer_merchant_info cmi on t.task_no=cmi.task_no and cmi.delete_flag=0
        left join(select task_no, max(id) id  from customer_audit_result where delete_flag = 0 group by task_no )car on car.task_no = t.task_no 
        left join customer_material_info m on m.customer_audit_result_id = car.id and m.delete_flag=0
         order by t.apply_time desc
 

优化后的查询时间大概在1S,但是之前都未曾考虑到搜索条件的问题(有关联表的字段需要作为搜索条件),所以将从表的搜索条件放在 INNER JOIN查询中

VERSION 3.0

 

select t.id,
t.task_no,
t.apply_time,
t.urgency_level,
t.product_id,
t.operator_id auditUserId,
b.customer_name,
b.customer_id_no,
b.customer_phone,
ifnull(f.merchant_code,cmi.merchant_code) merchant_code,
ifnull(f.merchant_name,cmi.merchant_name) merchant_name,
ifnull(f.store_code,cmi.store_code) store_code,
ifnull(f.store_name,cmi.store_name) store_name,
a.apply_amount,
a.pre_amount,
s.loan_amount,
s.term,
s.loan_time,
(select result from customer_audit_phone_result where task_no=t.task_no and delete_flag=0 order by id desc limit 1) phoneResult,
t.real_status audit_status,
car.id audit_result_id,
m.fitment_contract_amount
from customer_audit_task t
inner join (select si.id from customer_audit_task si
left join customer_base_info b on si.task_no = b.task_no and b.delete_flag=0
left join customer_apply_info a on si.task_no = a.task_no and a.delete_flag=0
left join customer_loan_success_info s on s.task_no = si.task_no and s.delete_flag=0
left join (select
ifnull(f.merchant_code,cmi.merchant_code) merchant_code,
ifnull(f.merchant_name,cmi.merchant_name) merchant_name,
ifnull(f.store_code,cmi.store_code) store_code,
f.task_no,
ifnull(f.store_name,cmi.store_name) store_name
from customer_fitment_info f
left join customer_merchant_info cmi on f.task_no = cmi.task_no
and f.delete_flag=0
and cmi.delete_flag=0
)ms on ms.task_no = si.task_no
where
find_in_set(si.product_id,#{productCodes})
and si.delete_flag=0
and si.real_status not in(100, 103, 1001, 1002)
<if test="custNameLike !=null and custNameLike != ''">
   and b.customer_name = #{custNameLike}
</if>
<if test="mobileLike !=null and mobileLike != ''">
   and b.customer_phone = #{mobileLike}
</if>
<if test="productLike !=null and productLike != ''">
   and si.product_id = #{productLike}
</if>
<!-- 资料审核中 -->
<if test="auditStatusLike == 11">
   and (si.real_status = 101 or si.real_status = 104)
</if>
<!-- 审批拒绝:资料审核拒绝或电核拒绝 -->
<if test="auditStatusLike == 16">
   and (si.real_status = 106 or si.real_status = 203)
</if>
<if test="auditStatusLike != 11 and auditStatusLike != 16 and auditStatusLike !=null and auditStatusLike != ''">
   and si.real_status = #{auditStatusLike}
</if>
<if test="applyTimeStartLike !=null and applyTimeStartLike != ''">
   <![CDATA[ and DATE_FORMAT(si.apply_time, '%Y-%m-%d') >= #{applyTimeStartLike} ]]>
</if>
<if test="applyTimeEndLike !=null and applyTimeEndLike != ''">
   <![CDATA[ and DATE_FORMAT(si.apply_time, '%Y-%m-%d') <= #{applyTimeEndLike} ]]>
</if>
<if test="userId !=null and userId != ''">
   and si.operator_id = #{userId}
</if>
<if test="loanTimeStartLike !=null and loanTimeStartLike != ''">
   <![CDATA[ and DATE_FORMAT(s.loan_time, '%Y-%m-%d') >= #{loanTimeStartLike} ]]>
</if>
<if test="loanTimeEndLike !=null and loanTimeEndLike != ''">
   <![CDATA[ and DATE_FORMAT(s.loan_time, '%Y-%m-%d') <= #{loanTimeEndLike} ]]>
</if>
<if test="merchantLike !=null and merchantLike != ''">
   and ms.merchant_code = #{merchantLike}
</if>
<if test="storeLike !=null and storeLike != ''">
   and ms.store_code = #{storeLike}
</if>
order by si.apply_time desc limit 0,10
)a on t.id = a.id
left join customer_base_info b on t.task_no = b.task_no and b.delete_flag=0
left join customer_apply_info a on t.task_no = a.task_no and a.delete_flag=0
left join customer_loan_success_info s on t.task_no = s.task_no and s.delete_flag=0
left join(select task_no, max(id) id  from customer_audit_result where delete_flag = 0 group by task_no )car on car.task_no = t.task_no
left join customer_material_info m on m.customer_audit_result_id = car.id and m.delete_flag=0
left join customer_fitment_info f on t.task_no = f.task_no and f.delete_flag=0
left join customer_merchant_info cmi on t.task_no=cmi.task_no and cmi.delete_flag=0
order by t.apply_time desc

通过EXPLAIN发现查询主要耗时集中在INNER JOIN子查询之中,主要耗时在ORDER BY 操作上(如果去掉耗时在0.01s左右得出),所以通过索引来优化LEFT JOIN中的排序(去除USING filesort部分

SELECT
        si.id
    FROM
        customer_audit_task si
    LEFT JOIN customer_base_info b ON si.task_no = b.task_no
    AND b.delete_flag = 0
     LEFT JOIN customer_apply_info a ON si.task_no = a.task_no
    AND a.delete_flag = 0
     LEFT JOIN customer_loan_success_info s ON s.task_no = si.task_no
     AND s.delete_flag = 0
         LEFT JOIN (
         SELECT
             ifnull(
                 f.merchant_code,
                 cmi.merchant_code
             ) merchant_code,
             ifnull(
                 f.merchant_name,
                 cmi.merchant_name
             ) merchant_name,
             ifnull(
                 f.store_code,
                 cmi.store_code
             ) store_code,
             f.task_no,
             ifnull(
                 f.store_name,
                  cmi.store_name
              ) store_name
          FROM
              customer_fitment_info f
         LEFT JOIN customer_merchant_info cmi ON f.task_no = cmi.task_no
         AND f.delete_flag = 0
         AND cmi.delete_flag = 0
     ) ms ON ms.task_no = si.task_no
    WHERE
        find_in_set(si.product_id, '1,6')
and real_status not in(100, 103, 1001, 1002)
    AND si.delete_flag = 0
     ORDER BY
         si.apply_time DESC
    LIMIT 0,10;

对于主表增加查询条件和排序字段索引
alter table customer_audit_task add index IDX_AUDIT_TASK_DELETEFLAG_APPLYTIME (delete_flag,  apply_time) ;

对于从表增加关于task_no字段的索引(只列出一个从表,其他从表同理)

alter table customer_loan_success_info add index IDX_LOAN_SUCCESS_INFO_TASKNO (task_no) ;
 

这次sql优化大概就是这样,从原来5万条记录的50S变成最后的0.01S,应该还有优化的空间(例如INNER JOIN中关联从表查过一次,外部查询时又left join了从表,这部分应该还有优化空间,大概还可以提高10%-20%(粗略统计)),主要的花费时间在如何在LEFT JOIN查询中建议合适的索引消除USING filesort,希望可以给看到的同学一点帮助。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值