且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

MySQL not exists 真的不走索引么?

更新时间:2022-08-22 13:32:07

以今天优化的SQL为例,优化前SQL为:

MySQL not exists 真的不走索引么?

优化效果:

优化前执行时间29秒以上,优化后1.2秒,优化提升25倍。

NOT EXISTS真的不走索引么?

查看两种SQL的执行计划!

使用NOT EXIST方式的执行计划:

MySQL not exists 真的不走索引么?

使用LEFT JOIN方式的执行计划:

MySQL not exists 真的不走索引么?

从执行计划来看,两个表都使用了索引,区别在于NOT EXISTS使用“DEPENDENT SUBQUERY”方式,而LEFT JOIN使用普通表关联的方式。


推荐看下:为什么索引能提高查询速度?


通过MySQL提供的Profiling方式来查看两种方式的执行过程。


使用NOT EXIST方式的执行过程:

MySQL not exists 真的不走索引么?

使用LEFT JOIN方式的执行过程:

从执行过程来看,LEFT JOIN方式的主要消耗在Sending data一项上(1.2s),而NOT EXISTS方式主要消耗在executeing和Sending data两项上,受限于Profiling只存放100行记录缘故。


从Profiling中只能看到47个” executeing和Sending data”的组合项(每个组合项约50us),通过执行计划看出,外表t_monitor的数据量为578436行,忽略统计信息不准情况下,使用NOT EXISTS方式应该会产生578436个” executeing和Sending data”的组合项,总计消耗时间=50μs*578436=28921800us=28.92s。


从上面执行过程可以推断出:


使用NOT EXISTS方式的执行性能严重依赖于NOT EXISTS子查询的执行次数即外层查询结果集的数据量。


当外层查询结果集的数据量N较小时执行性能较好,如有N=10执行时间为50μs*10=500us=0.005s,再加上一些额外消耗,执行结果也能在0.01秒或10毫秒内范围,这个响应时间应该能被大部分应用程序接受。


当外层程勋结果集的数据量N较大甚至上千万数据量时,NOT EXISTS的查询性能会变得非常糟糕,甚至会大量消耗服务器IO和CPU资源从而影响其他业务正常运行。


除上述问题外,在优化过程中发现本应该存储相同数据的resource_id列在两个表中定义不同,一表为VARCHAR而另外一表为BIGINT,外部结果集的字段类型和NOT EXIST字表中字段类型不同导致NOT EXISTS子查询中无法使用索引,使得子查询性能较差,最终影响整个查询的执行性能。


京东商城也曾出现过大量类似案例,一些表使用VARCHAR来存放订单号,而另一些表使用BIGINT来存放,在两表进行管理时性能极差,希望研发同事引以为戒。关注公众号Java技术栈回复m36获取一份MySQL研发军规。