mysql left join 导致的 Using join buffer (Block Nested Loop)

问题

在给运营部门写一个简单的两表join的时候,发现线上的数据库很快,本地的数据库很慢。

select  dt as '日期',avg(time1-time2)
from
(
select tradeid,time1 as dt
from table1
where 条件<略>
)a
 left join
(  
select tradeid,time2
from table2  where   条件<略>
)b
on a.tradeid=b.tradeid
group by dt

两个表经过条件筛选以后,数据量都差不多是5000条。 按理说5000 join 5000 应该不会很慢啊,同样的SQL,线上数据库跑30秒出结果,本地数据库跑了3000秒。

这就有意思了。最开始认为硬件资源不同,对比了一样,然后我怀疑是不是buffer pool size的问题,查了一下,果然本地的是100M,线上是1.7G。 好吧,我就用

SET GLOBAL innodb_buffer_pool_size=1*1024*1024*1024  

给本地改了。但是依然跑了3000秒。

执行计划

再对比下执行计划看看设呢么情况。

  • 线上的mysql


    5.6的执行计划
  • 本地的mysql


    5.7的执行计划

原来二者版本不一样,线上是5.6,本地是5.7.
5.6的版本是做成了临时表,然后对临时表join。 而5.7直接用了Using join buffer (Block Nested Loop)。

  • filtered=100% 表示右表没有应用索引下推(ICP),因为where条件没有索引。
  • 这个Using join buffer (Block Nested Loop)是因为右表没有在join列上建索引导致嵌套循环。

看来根源就在这儿了,首先没有ICP导致要全表800万数据到server层,其次left join 列没有索引又导致了嵌套循环。
如果把left join 改为join :


采用join

可见,mysql的优化器会先执行有索引的结果集,然后再与无索引表join。

总结

  1. 参与join的表,需要在连接条件上建索引
  2. left join 和 right join 会影响表连接的策略,具体来说,大结果集放在left join的前面,或者right join 的后面。比如在以上案例中,右表因为没有索引,可以认为是大结果集,所以应该把left join 改为 right join或者join(如果不影响逻辑的话)。最合理的当然是创建索引了。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容