1 关联查询的履行
关联查询的履行进程是:先遍历关联表t1(驱动表,全表扫描),然后根据从表t1中取出的每行数据中的a值,去表t2(被关联表,被驱动表)中查找满足条件的记录,可以走t2的索引搜索。在情势上,这个进程就跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为“Index Nested-Loop Join”,简称NLJ。在join语句的履行流程中,驱动表是走全表扫描,而被驱动表是走索引树搜索。
假定被驱动表的行数是M。每次在被驱动表查一行数据,要先搜索索引a,再搜索主键索引。每次搜索一棵树近似复杂度是以2为底的M的对数,记为log2M,所以在被驱动表上查一行的时间复杂度是2*log2M。
假定驱动表的行数是N,履行进程就要扫描驱动表N行,然后对每行,到被驱动表上匹配一次。
因此全部履行进程,近似复杂度是 N + N2log2M。明显,N对扫描行数的影响更大,因此应当让小表来做驱动表:N扩大1000倍的话,扫描行数就会扩大1000倍;而M扩大1000倍,扫描行数扩大不到10倍。
结论:如果使用join语句的话,需要让小表做驱动表,并且被驱动表的关联字段应当建立索引。一般来讲,除非有其他理由,否则只需要在关联顺序中的第二个表的相应列上创建索引,即在被驱动的表的关联字段简历索引。
2 没有索引的算法
如果,被驱动表的关联字段没有使用索引,那末mysql将使用另外一种Block Nested-Loop Join算法。
- 把表t1的数据读入线程内存join_buffer中,这只会将查询需要返回的列放入,如果我们的语句中写的是select *,就会把全部表t1放入了内存;
- 扫描表t2,把表t2中的每行取出来,跟join_buffer中的数据做对照,满足join条件的,作为结果集的一部份返回。
这个进程的流程图以下:
对应地,这条SQL语句的explain结果的Extra字段中将会展现:Block Nested Loop。在这个进程中,对表t1和t2都做了一次全表扫描,因此总的扫描行数是量表的数据总和M+N。由于join_buffer是以无序数组的方式组织的,因此对表t2中的每行,都要做100次判断,总共需要在内存中做的判断次数是:M* N次。
假定小表的行数是N,大表的行数是M,那末在这个算法里:
- 两个表都做一次全表扫描,所以总的扫描行数是M+N;
- 内存中的判断次数是M*N,虽然不需要读盘,但是需要占用大量CPU进行计算。
可以看到,调换这两个算式中的M和N没差别,因此这时候候选择大表或者小表做驱动表,履行耗时是一样的。
join_buffer的大小是由参数join_buffer_size设定的,默许值是256k。如果放不下表t1的所有数据话,策略很简单,就是将t1的数据分段放入、比较,假定表t1被分成了两次放入join_buffer中,那末会致使表t2会被扫描两次。虽然分成两次放入join_buffer,但是内存中判断等值条件的次数或者不变的,仍然是M*N次。
假定,驱动表的数据行数是N,需要分K段才能完成算法流程,K大于等于1,被驱动表的数据行数是M。注意,这里的K不是常数,N越大K就会越大。
所以,在这个算法的履行进程中:
- 扫描行数是 N+K*M;
- 内存判断 N*M次。
可以看到,如果join_buffer_size没有足够大(这是常见的情况),那末N越小,这样K就更小,扫描的行数才会更少,因此依然应当让小表当驱动表。而且K也是影响扫描行数的关键因素,这个值越小越好,如果N不变,那末影响K的就是join_buffer_size的大小。join_buffer_size越大,一次可以放入的行越多,分成的段数K也就越少,对被驱动表的全表扫描次数就越少。
因此,如果你的join语句很慢,除让小表当驱动表,还有就把join_buffer_size改大。
如果肯定“小表”呢?除总行数以外,还应当是两个表依照各自的条件过滤,过滤完成以后,再计算参与join的各个字段的总数据量(由于还要放入内存中),数据量小的那个表,就是“小表”,应当作为驱动表。
实际在查询优化时,如果join不是使用的Index Nested-Loop Join算法,则应当尽可能改成使用该算法。
到此这篇关于MySQL JOIN关联查询的原理及优化的文章就介绍到这了,更多相关MySQL JOIN关联查询 内容请搜索之前的文章或继续浏览下面的相关文章希望大家以后多多支持!
本文来源:https://www.yuntue.com/post/152185.html | 云服务器网,转载请注明出处!

微信扫一扫打赏
支付宝扫一扫打赏