Index Nested-Loop Join
可以用上被驱动表(从表)的索引,所以我们称之为“Index Nested-Loop Join”,简称NLJ。
join语句的执行过程
-
从表t1中读入一行数据 R;
-
从数据行R中,取出a字段到表t2里去查找;
-
取出表t2中满足条件的行,跟R组成一行,作为结果集的一部分;
-
重复执行步骤1到3,直到表t1的末尾循环结束。
结论
“可以使用被驱动表的索引”的前提下我们可以知道两点:
- 使用join比单个执行的性能要好
- 使用join,需要让小表做驱动表(主表)
Simple Nested-Loop Join
如果被驱动表关联字段上没有索引,那么每次在驱动表做匹配的时候就会做一次全表扫描。
MySQL也没有使用这个Simple Nested-Loop Join算法,而是使用了另一个叫作“Block Nested-Loop Join”的算法,简称BNL。
Block Nested-Loop Join
被驱动表上没有可用的索引,算法的流程是这样的:
- 把主表获取的数据读入线程内存join_buffer中,如果是select *不带条件,那么整个表都会放入内存;
- 扫描从表,把主表中的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。
join_buffer
join_buffer是以无序数组的方式组织的,因此对主表中的每一行,都要做join_buffer中的所有数据进行判断,总共需要在内存中做的判断次数是:T1总数*T2总数次。
join_buffer的大小是由参数join_buffer_size设定的,默认值是256k。如果放不下表t1的所有数据话,策略很简单,就是分段放。
超过join_buffer size执行过程:
- 扫描主表放入主表数据进join_buffer中,放满为止;
- 扫描从表,取出数据,跟join_buffe钟数据比对,满足的数据作为一部分返回
- 清空join_buffer
- 继续扫描主表剩下的数据放入join_buffer钟,并继续第二步。
结论
- 如果可以使用Index Nested-Loop Join算法,也就是说可以用上被驱动表上的索引,其实是没问题的;
- 如果使用block nested-loop 算法,扫描行数就会过多。尤其是大表上的join,从表可能要扫很多次,会占用大量资源,尽量不用
- 如果是Index Nested-Loop Join算法,应该选择小表做驱动表;
- 如果是Block Nested-Loop Join算法:当join_buffer_size足够大的时候 是一样的; join_buffer_size不够大的时候应选择小表 (计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表。)
长事务的影响
- 如果前面的语句有更新,意味着它们在占用着行锁,会导致别的语句更新被锁住
- 当然读的事务也有问题,就是会导致undo log不能被回收,导致回滚段空间膨胀。