一次显著的性能提升,从8s到0.7s

今天从实战的角度出发,给大家分享一下如何做SQL调优。

经过两次优化之后,慢SQL的性能显著提升了,耗时从 8s 优化到了 0.7s

1 案发现场

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

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

复制代码
SELECT count(*)
FROM spu s1
WHERE 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张表都走了 索引

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

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

2 第一次优化

这条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倍多,但还是不够快,还需要做进一步优化。

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调优是一个比较复杂的问题,需要考虑的因素有很多,有可能需要多次优化才能满足要求。

相关推荐
爱划水不秃头的程序员1 小时前
堆 栈 和常量池的关系是啥 是jvm对他们进行的划分吗
java
周GZ1 小时前
4个提问,deepseek讲解Java中的线程池
java
LuTshoes1 小时前
context 上下文工程
java·人工智能·spring·ai
wuminyu1 小时前
Markword在紧凑对象头上的实现原理剖析
java·linux·c语言·jvm·c++
君顾12 小时前
24小时自助健身房系统开发实战与完整指南
java·开发语言·健身房
千舟软件2 小时前
【宿舍管理小程序】新生入住不靠纸质表
大数据·运维·服务器·科技·业界资讯
oradh2 小时前
Oracle联机日志文件损坏故障总结
数据库·oracle·oracle联机日志文件损坏
鹿角片ljp2 小时前
LeetCode 78:子集|回溯、选与不选、递归和path快照
java·数据结构·算法
IvorySQL2 小时前
PostgreSQL 日报|8 字节 TOAST 值支持(9 月 16 日)
数据库·人工智能·postgresql
精益数智工坊2 小时前
云计算环境元数据是什么意思?云计算元数据管理怎么做?
大数据·人工智能·数据挖掘·数据可视化