Redian新闻
>
不要用 in + 子查询

不要用 in + 子查询

科技

你好,我是yes。

前两天我的 VIP 用户向我抛出了一个 SQL 问题,他的 MySQL 是 8.x版本:

大概意思如下 sql :

select * from A where id in (
  select max(idas id from A 
   where task_id in(1,2,3
    group by task_id
);

这个 A 表中是有 task_id 这个索引的。

一眼看去这个查询没啥问题,子查询里会利用 task_id 这个索引,然后外面的这个查询会上 id 这个主键索引。

而事实是:

一看 explain ,果然走了全表扫描:

可以看到子查询用上了 taskId 这个索引,而外部的查询走的是全表扫描,大概扫描了 890W 的数据,所以查询花了 1 分 30 秒。

那么问题来了:明明有主键索引不用,mysql 为什么要选择全表?

我也不知道。

MySQL 有个优化器,它会决定最终以怎么样的形式、选择那个索引来生成最终的执行计划。

对将要执行的 SQL 而言,优化器会有成本模型,它会根据当前表的一些估算值结合当前的 SQL 语句进行打分,比如如果用了索引 A 需要多少 I/O 成本、CPU 成本,如果用索引 B 要多少I/O、CPU成本。

总而言之,它有自己的一套规则,会根据估计值预算成本,根据成本最终生成执行计划。

篇幅有限具体不多介绍,有兴趣的话看下官网:https://dev.mysql.com/doc/refman/8.0/en/cost-model.html

既然是预算,那就有可能不准,所以有时候就会产生该走索引却全表扫描的情况(因为算出的成本走全表扫描反而更低)。

所以针对上面这种情况,我让他加了强制用主键的操作 force index(PRIMARY),但是并没有生效,还是走了全表。

可以看到扫描了大约 996w 行数据(表数据有新增,所以从800w多变成了900多w)。

我自己估计:大致就是因为子查询的结果不确定,MySQL 不知道 in 的值到底有多大,所以保守型的选择了全表(我猜的,具体的逻辑估计得看源码,不过最终表现的事实确实如此)。

问题就在 in 的值大小不确定。

通过子查询,我们自己其实也无法保证 in 的值到底是多少。

所以最后的方案是抛弃 in 的方式,采用 inner join 的方式来改造 sql  实现。

改造 SQL 如下:

select * from A a inner join (
  select max(idas id from A 
   where task_id in(1,2,3group by task_id
) b 
on a.id = b.id;

这样改造以后,进行了一波 explain,结果如下:

查询结果秒出,可以看下第二行,用上了主键索引,全部扫描的行数加起来也就 600 多。

所以我们换了一种方式来使得 MySQL 正常的用上索引且所要的结果是一致的。

这里也建议,如果 sql 是包含子查询的 in 查询,那么最好换成 join 的方式,因为我们不能保证 in 的查询一定会用上索引,万一来个全表扫描,在表数据量比较大的场景,很容易产生阻塞,多来几个这样的阻塞,数据库连接不可用,服务可能就挂了。

在测试环境看起来没问题,一到生产就 GG。

所以能不用 in + 子查询,就不要用,可以用 join 来替代实现

最近一个月还有很多小伙伴来私聊我问我关于 VIP 一对一问答服务的,这里再宣传一下:我的VIP问答服务,关于原理解答、面试等。

我是yes,从一点点到亿点点我们下篇见~


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
MyBatis-Plus 还手写 Join 联表查询?一个依赖轻松搞定,真香!代糖下岗?WHO说不要用代糖减肥警惕!千万不要用免费充电站! 很容易被黑客盗窃重要信息...不要用表面光鲜的数据掩盖问题所在向量数据库?不要投资!不要投资!不要投资!不要扑空!今天(周一)很多店铺可能不开门 外出要提前查询谷歌顶级AI专家加入OpenAI 曾警告谷歌不要用ChatGPT数据训练Bard张彬彬,同一招不要用到两个人身上疫情后第一个期末考!用这个神器帮孩子查缺补漏,别带着薄弱知识点进入下一学年5033 血壮山河之武汉会战 鏖战幕府山 2绿卡/F/H/J/M等签证在美加办理日本签证指南(单次签证可以在线缴费、行程不要用CHATGPT写)FBI警示:不要用公用免费充电站,易被黑客窃信息,请自备充电器 | 北美候鸟红脖是美国的雷锋美国妹子查看在亚马逊上8年的总花费,惊到说不出话来了...又是一年高考时,请不要用孩子18岁的成就定义终身成功博尔赫斯Set Me Free -BTS@上海市民朋友们,如何查询出生医学证明档案?超实用攻略来了→不只有ChatGPT,华人牛爸巧用AI技术给孩子查缺补漏!FBI警告!免费公共充电站不要用,会被感染恶意软件遇见它,千万不要用手拍!高开即将结束,请不要用孩子18岁的成就定义终身成功学员故事|不要海投!不要海投!不要海投!苏丹一实验室被武装分子查封,有“高度生物危害”风险澳洲母亲怒斥:Coles,不要用避孕套代替糖果!“不要辣”英语怎么说?no spicy?不要葱不要香菜呢?专家警告!永远不要用机场车站的免费充电器充电,不然很危险!美国妹子查亚马逊网8年的总花费,惊到无语!纽约客快查查!速看!江苏13市高三一模+二模划线汇总(附近3年高校录取分数及位次查询表)做人,用心,不要用心计国家移民管理局:多渠道可在线查询十年内出入境记录,办理普通护照多久可以拿到?谈论碳排放,不要用私人飞机碰瓷三只狗高尔夫降维打球SIGIR 2023 | 30万真实查询、200万互联网段落,中文段落排序基准数据集发布Agustín Hernández:中美洲建筑背景下的未来主义巨构
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。