问题
在给运营部门写一个简单的两表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
-
本地的mysql
原来二者版本不一样,线上是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 :
可见,mysql的优化器会先执行有索引的结果集,然后再与无索引表join。
总结
- 参与join的表,需要在连接条件上建索引
- left join 和 right join 会影响表连接的策略,具体来说,大结果集放在left join的前面,或者right join 的后面。比如在以上案例中,右表因为没有索引,可以认为是大结果集,所以应该把left join 改为 right join或者join(如果不影响逻辑的话)。最合理的当然是创建索引了。