从8s到0.7s,又积累了不少慢查询SQL优化经验!

苏三 2023-12-06 11:36:54

 

最近笔者在公司优化了一些慢查询SQL,积累了一些SQL调优的实战经验。经过两次优化之后,慢SQL的性能显著提升了,耗时从8s优化到了0.7s。

这篇文章从实战的角度出发,分享一下如何做SQL调优,希望对大家有所帮助。

 

一、案发现场

 

前几天,我收到了一封报警邮件,提示有一条慢查询SQL。

 

我打开邮件查看了详情,那条SQL大概是这样的:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
SELECT count(*)FROM spu s1WHERE EXISTS ( SELECT * FROM sku s2  INNER JOIN mall_sku s3 ON s3.sku_id = s2.id WHERE s2.spu_id = s1.id  AND s2.status = 1  AND NOT EXISTS (   SELECT *   FROM supplier_sku s4   WHERE s4.mall_sku_id = s3.id    AND s4.supplier_id = 123456789    AND s4.status = 1  ))

 

这条SQL的含义是统计id=123456789的供应商,未发布的spu数量是多少。

 

这条SQL的耗时竟然达标了8s,必须要做优化了。

 

我首先使用explain关键字查询该SQL的执行计划,发现spu表走了type类型的索引,而sku、mall_sku、supplier_sku表都走了ref类型的索引。

 

也就是说,这4张表都走了索引。

 

不是简单的增加索引,就能解决的事情。

 

那么,接下来该如何优化呢?

 

二、第一次优化

 

这条SQL语句,其中两个exists关键字引起了我的注意。

 

一个exists是为了查询存在某些满足条件的商品,另一个not exists是为了查询出不存在某些商品。

 

这个SQL是另外一位已离职的同事写的。

 

不清楚spu表和sku表为什么不用join,而用了exists。

 

我猜测可能是为了只返回spu表的数据,做的一种处理。如果join了sku表,则可能会查出重复的数据,需要做去重处理。

 

从目前看,这种写性能有瓶颈。

 

因此,我做出了第一次优化。

 

使用join + group by组合,将sql优化如下:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
SELECT count(*) FROM(  select s2.spu_id from spu s1  inner join from sku s2  inner join mall_sku s3 on s3.sku_id=s2.id  where s2.spu_id=s1.id ans s2.status=1  and not exists   (     select * from supplier_sku s4     where s4.mall_sku_id=s3.id     and s4.supplier_id=...  )  group by s2.spu_id) a

 

文章中有些相同的条件省略了,由于spu_id在sku表中是增加了索引的,因此group by的性能其实是挺快的。

 

这样优化之后,sql的执行时间变成了2.5s。

 

性能提升了3倍多,但是还是不够快,还需要做进一步优化。

 

三、第二次优化

 

还有一个not exists可以优化一下。

 

如果是小表驱动大表的时候,使用not exists确实可以提升性能。

 

但如果是大表驱动小表的时候,使用not exists可能有点弄巧成拙。

 

这里exists右边的sql的含义是查询某供应商的商品数据,而目前我们平台一个供应商的商品并不多。

 

于是,我将not exists改成了not in。

 

sql优化如下:

 

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
SELECT count(*) FROM(  select s2.spu_id from spu s1  inner join from sku s2  inner join mall_sku s3 on s3.sku_id=s2.id  where s2.spu_id=s1.id ans s2.status=1  and s3.id not IN   (     select s4.mall_sku_id      from supplier_sku s4     where s4.mall_sku_id=s3.id     and s4.supplier_id=...  )  group by s2.spu_id) a

 

这样优化之后,该sql的执行时间下降到了0.7s。

 

之后,我再用explain关键字查询该SQL的执行计划。

 

发现spu表走了全表扫描,sku表走了eq_ref类型的索引,而mall_sku和supplier_sku表走了ref类型的索引。

 

可以看出,有时候sql语句走了4个索引,性能未必比走了3个索引好。

 

多张表join的时候,其中一张表走了全表扫描,说不定整个SQL语句的性能会更好,我们一定要多测试。

 

说实话,SQL调优是一个比较复杂的问题,需要考虑的因素有很多,有可能需要多次优化才能满足要求。

 
作者丨苏三
来源丨公众号:苏三说技术(ID:susanSayJava)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
最新评论
访客 2023年08月20日

230721

访客 2023年08月16日

1、导入Mongo Monitor监控工具表结构(mongo_monitor…

访客 2023年08月04日

上面提到: 在问题描述的架构图中我们可以看到,Click…

访客 2023年07月19日

PMM不香吗?

访客 2023年06月20日

如今看都很棒

活动预告