MySQL5.7和8.0中in和exists关键字

时间:2024-04-07 11:18:12

之前看了网上很多关于in和exists性能相关的博客,但总感觉自己测试的结果和他们说的不太一样,所以写下这篇博客,记录下自己测试后得出的总结。特别强调,MySQL5.7和8.0中对这两个关键字有不同的优化,所以我要分开两个版本来讨论。

数据准备

我准备了一张tb_class表和一张tb_stu表
MySQL5.7和8.0中in和exists关键字
MySQL5.7和8.0中in和exists关键字
注:600个班级,12万个学生。tb_stu是大表,tb_class是小表。为了避免索引优化带来的影响,所以我两张表的id都没有设置主键和索引。

MySQL5.7测试

测试查询效率

我们首先测试一下当外层表时小表的时候,in和exists谁的效率比较高。

MySQL5.7和8.0中in和exists关键字
在MySQL5.7中,当外层的表是小表的时候,in的效率会优于exists。

用explain看一下两个sql的执行计划
MySQL5.7和8.0中in和exists关键字
从执行计划中看到,exists语句中的select_type是 DEPENDENT SUBQUERY ,我认为性能的消耗主要是在这里,因为 DEPENDENT SUBQUERY 是非常消耗性能的。

那么当外层的表时大表的时候,in和exists的效率又是谁比较高呢?

在MySQL5.7中,当外层的表是大表的时候,in的效率依然高于exists。

MySQL5.7和8.0中in和exists关键字
可以看到当外层的表是大表的时候,执行效率就非常低了,但是in的效率依旧高于exists。
使用explain查看一下两个sql的执行计划

MySQL5.7和8.0中in和exists关键字
经过以上的测试,我发现:外层表不论是大表还是小表,in的效率都要高于exists。网上说什么小表驱动大表,感觉那帮人也没真正自己测试过。

查询效率低的优化策略

从上面的测试我们可以看出来,当大表的数据量超过10W+时,查询效率已经开始变低了(我把大于2秒的查询定为慢查询)。
那么优化方案是什么呢,我的策略是加索引。
我们先在tb_stuclass_id字段上加上btree索引
添加上索引之后,再来观察一下外层表是小表时候的查询效率

MySQL5.7和8.0中in和exists关键字

加上索引之后的效果就非常明显了,原先exists查询耗时2s多,现在只耗时0.004s

MySQL5.7和8.0中in和exists关键字
通过explain可以发现,rows从原来的12w+变成了只有130。

我们再来对比一下外层表是大表的时候,索引是否能有效。
MySQL5.7和8.0中in和exists关键字

震惊!!!exists居然还是耗时8s多!!!
我猜测原因是:当使用exists时,外层表的索引无法在子查询中生效
查看explain的结果
MySQL5.7和8.0中in和exists关键字

MySQL8.0测试

测试查询效率

我将数据库换成MySQL8.0,先测试一下外层表是小表的时候,in和exists的查询效率
MySQL5.7和8.0中in和exists关键字
我们发现,在MySQL8.0中,in和exists的执行效率是一样高的
甚至我使用explain查看的执行计划都是一样的。

MySQL5.7和8.0中in和exists关键字
将外层表改为大表,再测试一下:

MySQL5.7和8.0中in和exists关键字

查询效率依旧一样快有木有!
他俩的explain执行计划也是一样的:
MySQL5.7和8.0中in和exists关键字

最后总结

  1. 在MySQL5.7中,in的效率要高于exists,但在8.0中,他俩的效率是一样的
  2. 在MySQL5.7中,外层表的字段使用in查询不会使这个字段的索引失效,但是如果使用exists就会使索引失效了
  3. MySQL8.0的查询效率真的比5.7好很多,如果是新项目的话,强烈建议使用MySQL8.0