之前看了网上很多关于in和exists性能相关的博客,但总感觉自己测试的结果和他们说的不太一样,所以写下这篇博客,记录下自己测试后得出的总结。特别强调,MySQL5.7和8.0中对这两个关键字有不同的优化,所以我要分开两个版本来讨论。
数据准备
我准备了一张tb_class表和一张tb_stu表
注:600个班级,12万个学生。tb_stu是大表,tb_class是小表。为了避免索引优化带来的影响,所以我两张表的id都没有设置主键和索引。
MySQL5.7测试
测试查询效率
我们首先测试一下当外层表时小表的时候,in和exists谁的效率比较高。
在MySQL5.7中,当外层的表是小表的时候,in的效率会优于exists。
用explain看一下两个sql的执行计划
从执行计划中看到,exists语句中的select_type是 DEPENDENT SUBQUERY
,我认为性能的消耗主要是在这里,因为 DEPENDENT SUBQUERY
循环嵌套子查询,其效率可想而知。
那么当外层的表时大表的时候,in和exists的效率又是谁比较高呢?
在MySQL5.7中,当外层的表是大表的时候,in的效率依然高于exists。
可以看到当外层的表是大表的时候,执行效率就非常低了,但是in的效率依旧高于exists。
使用explain查看一下两个sql的执行计划
经过以上的测试,我发现:外层表不论是大表还是小表,in的效率都要高于exists。网上说什么小表驱动大表,感觉那帮人也没真正自己测试过。
查询效率低的优化策略
从上面的测试我们可以看出来,当大表的数据量超过10W+时,查询效率已经开始变低了(我把大于2秒的查询定为慢查询)。
那么优化方案是什么呢,我的策略是加索引。
我们先在tb_stu的class_id字段上加上btree索引
添加上索引之后,再来观察一下外层表是小表时候的查询效率
加上索引之后的效果就非常明显了,原先exists查询耗时2s多,现在只耗时0.004s
通过explain可以发现,rows从原来的12w+变成了只有130。
我们再来对比一下外层表是大表的时候,索引是否能有效。
震惊!!!exists居然还是耗时8s多!!!
我猜测原因是:当使用exists时,外层表的索引无法在子查询中生效
查看explain的结果
MySQL8.0测试
测试查询效率
我将数据库换成MySQL8.0,先测试一下外层表是小表的时候,in和exists的查询效率
我们发现,在MySQL8.0中,in和exists的执行效率是一样高的。
甚至我使用explain查看的执行计划都是一样的。
将外层表改为大表,再测试一下:
查询效率依旧一样快有木有!
他俩的explain执行计划也是一样的:
最后总结
- 在MySQL5.7中,in的效率要高于exists,但在8.0中,他俩的效率是一样的
- 在MySQL5.7中,外层表的字段使用in查询不会使这个字段的索引失效,但是如果使用exists就会使索引失效了
- MySQL8.0的查询效率真的比5.7好很多,如果是新项目的话,强烈建议使用MySQL8.0