暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

MySQL优化生产实践-MySQL 优化器负优化产生超慢查询(三),优化后性能提升 47倍

原创 chengang 2025-09-26
612

1、问题复现

今天发现一个问题,同样的SQL,当in条件内只有一个的时候很慢很慢。当in条件有多个时,反而很快

慢的SQL如下

explain select * from erp_goods_stock_inout_deal force index(ix_profileid_cid_billtype_billdate) where profileid = 20568 and cid in (374040) and billdate between '2025-04-04' and '2025-04-04' order by profileid ,cid,billtype desc,billdate desc;

快的SQL如下:

explain select * from erp_goods_stock_inout_deal force index(ix_profileid_cid_billtype_billdate) where profileid = 20568 and cid in (374040,374080) and billdate between '2025-04-04' and '2025-04-04' order by profileid ,cid,billtype desc,billdate desc;

两个SQL一模一样
仅有的区别是
慢的SQL where 条件处是 cid in (557279)
快的SQL where 条件处是 cid in (557279,557287)

goods_stock_inout_deal 表 记录数为:3402931

慢的SQL执行需要13秒
而快的SQL只需要0.281秒 两者相差47倍

2、问题排查

a、排查第一步 查看执行计划

慢SQL的执行计划如下
image.png
快SQL执珩计划如下
image.png

通过执行计划比对,两条SQL 扫描行数一样,且慢SQL还消除了排序,从执行计划上看明显慢SQL还更优,那需要继续深入排查

b、排查第二步 查看实际耗时

慢SQL的实际耗时
image.png

快SQL的实际耗时
image.png

通过实际耗时对比
慢SQL在扫描 goods_stock_inout_deal 表时用了大量时间 13秒
快SQL在扫描 goods_stock_inout_dea 表时只用了260毫秒

快SQL多了一个Using index condition 这个即常说的ICP
那么可以推测是因为ICP的优化节约了大量时间

c、排查第三步 确认推测

我们来看此索引各字段的区分度

select count(*) from goods_stock_inout_deal force index(ix_profileid_cid_billtype_billdate) where profileid = 20568 and cid in (374040);

慢SQL索引扫描后符合条件的有 589068

select count(*) from goods_stock_inout_deal force index(ix_profileid_cid_billtype_billdate) where profileid = 20568 and cid in (374040,374080) and billdate between '2025-04-04' and '2025-04-04' order by profileid ,cid,billtype desc,billdate desc;

慢SQL扫描索引后符合条件的只有262行。

符合推测,此语句就是因为ICP优化过滤了大量的行

3、性能差异原因

我们来复原两条SQL的执行过程
1、MySQL扫描goods_stock_inout_deal 表的索引 ix_profileid_cid_billtype_billdate共1171124 通过profileid与cid 定位查找共有 589068条记录符合条件。再拿到这589068条记录回表。在server层过滤billdate字段,最终返回262行

2、MySQL扫描goods_stock_inout_deal 表的索引 ix_profileid_cid_billtype_billdate共1171124 通过profileid与cid 定位查找共有 589068条,在定位到 589068记录时通过ICP(索引下推)丢弃到不符合billdate字段的行。丢弃后,共有262行记录,再回表拿到其它字段内容,返回客户端

两者最大的区别是慢SQL有589068条记录需要回表,而快SQL只需要262行记录需要回表

4、解决方案

很明显为什么MySQL在倒序排序上就不能使用ICP 这点MySQL的确应该优化
当前解决方案
1、改变索引 建立新索引idx(profileid,cid,illdate)
2、in条件 如果只有一个。就写两个重复的。通过实验可以确定 in(两个一样的值) 也不能走backward index scan

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论