前面我们我们介绍了索引的优化,这篇文章我们介绍一下关联查询的优化。
我们首先准备一下数据库表,然后各插入20条数据
采用左外连接
EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
我们可以看到上面的type字段为all即为全表扫描。
ALTER TABLE book ADD INDEX Y ( card); #【被驱动表】,可以避免全表扫描
EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
ALTER TABLE `type` ADD INDEX X (card); #【驱动表】,无法避免全表扫描
EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
紧接着我们做如下操作
DROP INDEX Y ON book;
EXPLAIN SELECT SQL_NO_CACHE * FROM `type` LEFT JOIN book ON type.card = book.card;
所以,我们一定要在被驱动表上加上索引。
采用内连接
drop index X on type;
drop index Y on book;(如果已经删除了可以不用再执行该操作)
EXPLAIN SELECT SQL_NO_CACHE * FROM type INNER JOIN book ON type.card=book.card;
添加索引优化
ALTER TABLE book ADD INDEX Y ( card);
EXPLAIN SELECT SQL_NO_CACHE * FROM type INNER JOIN book ON type.card=book.card;
ALTER TABLE type ADD INDEX X (card);
EXPLAIN SELECT SQL_NO_CACHE * FROM type INNER JOIN book ON type.card=book.card;
DROP INDEX X ON `type`;
EXPLAIN SELECT SQL_NO_CACHE * FROM TYPE INNER JOIN book ON type.card=book.card;
ALTER TABLE `type` ADD INDEX X (card);
EXPLAIN SELECT SQL_NO_CACHE * FROM `type` INNER JOIN book ON type.card=book.card;
![](https://img-blog.csdnimg.cn/34bc84343f2543c4b048e9f1748b209a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aSP5biFSmF2YU0=,size_20,color_FFFFFF,t_70,g_se,x_16)
2.Simple Nested-Loop Join(简单嵌套循环连接)
算法相当简单,从表A中取出一条数据1,遍历表B,将匹配到的数据放到result。以此类推,驱动表A中的每一条记录与被驱动表B的记录进行判断
![](https://img-blog.csdnimg.cn/71d44ca47f154ab79cd7c301052f46c9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aSP5biFSmF2YU0=,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/df9b535b790e416f95fd643db05c9ab1.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aSP5biFSmF2YU0=,size_20,color_FFFFFF,t_70,g_se,x_16)
3.Index Nested-Loop Join(索引嵌套循环)
其优化的思路主要是为了减少内层表数据的匹配次数,所以要求被驱动表上必须有索引才行。通过外层表匹配条件直接与内存表索引进行匹配,避免和内层表的每条记录去进行比较,这样极大的减少了对内层表的匹配次数。
驱动表中的每条记录通过被驱动表的索引进行访问,因为索引查询的成本比较固定,故mysql优化器倾向于使用记录数少的表作为驱动表。
如果被驱动表加上索引,效率是非常高的,但如果索引不是主键索引,所以还得进行一次回表查询。相比,被驱动表的索引是主键索引,效率会更高。
![](https://img-blog.csdnimg.cn/c4aef9b95c844098b2088eb53ec3aa79.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aSP5biFSmF2YU0=,size_20,color_FFFFFF,t_70,g_se,x_16)
join_buffer_size的最大值在32为系统中可以申请4G,在64为操作系统中可以申请大于4G的空间。
5.小结
①整体效率比较 INLJ>BNLJ>SNLJ
②永远用小结果集驱动大结果集,其本质就是减少外层循环的数据量。小的度量单位指的是表行数*每行大小。
③为被驱动表匹配的条件增加索引,减少内存循环匹配次数
④增大join buffer 大小,一次性缓存数据越多,那么内层包的扫表次数就越少。
⑤减少驱动表不必要的字段查询,字段越少,join buffer缓存的数据越多
6.Hash Join
从mysql的8.0.20版本开始就废弃了BNLJ,因为从0.18版本就开始加入了hash join默认都会使用hash join。
①Nested Loop:对于被连接数据子集较小的情况下,是个比较好的选择。
②hash join 是做大数据集连接时的常用方式,优化器使用两个表中较小(相对较小)的表利用join key在内存中建立散列表,然后扫描较大的表并探测散列表,找出与hash表匹配的行。
Ⅰ 这种方式适用于较小的表完全可以放于内存中的情况,这样总成本就是访问两个表的成本之和。
Ⅱ在表很大的情况下并不能完全放入内存,这时优化器会将它分割成若干个不同分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要求有较大的临时段从而尽量提高IOS的性能。
Ⅲ它能够很好的工作于没有索引的大表和并行查询环境中,并提供最好的性能。大多数都说他是join的重型升降机。hash join只能应用于等值连接,这是由hash的特点决定的。