最近在做SQL优化中,把传统分页limit offset,size转变成内连接查询id实现,具体可见上一篇博文
关于内连接的SQL,同事提出了一些疑问,哪张表是驱动表?放在前边的吗?


这里先放结论(这个结论没有考虑join的时候on的条件对结果的影响,实践中给出了一些参考):
1、当连接查询没有where条件时,
左连接查询时,前面的表是驱动表,后面的表是被驱动表;
右连接查询时相反,前面的表是被驱动表,后面的表是驱动表;
内连接查询时,哪张表的数据较少,哪张表就是驱动表
2、当连接查询有where条件时,带where条件的表是驱动表,否则是被驱动表
3、explain执行计划第一行的是驱动表
4、可以使用straight_join强行指定驱动表

理论基础

从表A搂取数据,然后到表B中匹配

1、Simple Nested-Loop Join

for(表A) {
for(表B)
}
被驱动表上没有索引,每次循环都会全表扫描
如果只看结果的话,这个算法是正确的,而且这个算法也有一个名字,叫做“Simple Nested-Loop Join”。当然,MySQL 也没有使用这个 Simple Nested-Loop Join 算法,而是使用了另一个叫作“Block Nested-Loop Join”的算法,简称 BNL。

2、Index Nested-Loop Join

用到了被驱动表的索引
这个过程是先遍历表 t1,然后根据从表 t1 中取出的每行数据中的 a 值,去表 t2 中查找满足条件的记录。在形式上,这个过程就跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为“Index Nested-Loop Join”,简称 NLJ。

3、Block Nested-Loop Join

当没有用到被驱动表上的索引时,计算流程如下:

1、把表 t1 的数据读入线程内存 join_buffer 中,由于我们这个语句中写的是 select *,因此是把整个表 t1 放入了内存;

2、扫描表 t2,把表 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回。

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_连接查询


从时间复杂度上来说,BNL和Simple Nested-Loop Join算法是一样的。但是,Block Nested-Loop Join 算法的这 10 万次判断是内存操作,速度上会快很多,性能也更好

一些实践

设定大表a,小表b
1、 内连接 无where条件 小表在右,大表在左

select *
from compensate_order a 
inner join log b 
on a.compensate_id = b.after_sale_id

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_数据_02

select *
from compensate_order a inner join log b

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_内连接_03

2、内连接 无where条件 小表在左,大表在右

select *
from log b 
inner join compensate_order a 
on a.compensate_id = b.after_sale_id

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_内连接_04

select *
from log b inner join compensate_order a

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_数据_05

可以看到和情景1没有区别

3、内连接 大表有where条件 小表在右,大表在左

explain
select *
from compensate_order a 
inner join log b
where a.id > 769

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_数据_06

explain
select *
from compensate_order a 
inner join log b 
on a.compensate_id = b.after_sale_id
where a.id > 769

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_连接查询_07


4、内连接 小表有where条件 小表在右,大表在左

explain
select *
from compensate_order a 
inner join log b
where b.id > 14

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_连接查询_08

explain
select *
from compensate_order a 
inner join log b 
on a.compensate_id = b.after_sale_id
where b.id > 14

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_数据_09

3、left join 小表在左,大表在右

mysql中left join被驱动表不走索引 mysql驱动表和被驱动表_连接查询_10


4、left join 小表在右,大表在左