mysql数据库sql优化(二)看这里之关联查询优化

前面我们我们介绍了索引的优化,这篇文章我们介绍一下关联查询的优化。

我们首先准备一下数据库表,然后各插入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;

可以看到第二行的 type 变为了 ref rows 也变成了优化比较明显。这是由左连接特性决定的。 LEFT JOIN 条件用于确定如何从右表搜索行,左边一定都有,所以 右边是我们的关键点,一定需要建立索引 。
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;(如果已经删除了可以不用再执行该操作)
换成 inner join MySQL 自动选择驱动表)
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;

 

join 语句原理
join方式连接多个表,本质就是各个表之间数据的循环匹配。mysql5.5版本之前,mysql只支持一种表间关联方式,就是嵌套循环。如果关联表中的数据量很大,则join关联的执行时间会非常长。在mysql5.5以后的版本中,mysql通过BNLJ算法来优化嵌套执行。
1.驱动表和被驱动表
驱动表就是主表,被驱动表就是从表、非驱动表
①对于内连接来说
select * from A join B on...
A一定是驱动表吗?不一定,优化器会根据你查询语句做优化,决定先查询哪张表,先查询的那张表就是驱动表,反之就是被驱动表。通过explain关键字可以查看。
②对于外连接来说
select * from A left join B  on...
或select * from B right A  on...
通常,大家会认为A就是驱动表,B就是被驱动表,但也未必。测试如下:

 

2.Simple Nested-Loop Join(简单嵌套循环连接)

算法相当简单,从表A中取出一条数据1,遍历表B,将匹配到的数据放到result。以此类推,驱动表A中的每一条记录与被驱动表B的记录进行判断

 

 3.Index Nested-Loop Join(索引嵌套循环)

其优化的思路主要是为了减少内层表数据的匹配次数,所以要求被驱动表上必须有索引才行。通过外层表匹配条件直接与内存表索引进行匹配,避免和内层表的每条记录去进行比较,这样极大的减少了对内层表的匹配次数。

 驱动表中的每条记录通过被驱动表的索引进行访问,因为索引查询的成本比较固定,故mysql优化器倾向于使用记录数少的表作为驱动表。

如果被驱动表加上索引,效率是非常高的,但如果索引不是主键索引,所以还得进行一次回表查询。相比,被驱动表的索引是主键索引,效率会更高。

两个结论:
1. 使用 join 语句,性能比强行拆成多个单表执行 SQL 语句的性能要好;
2. 如果使用 join 语句的话,需要让小表做驱动表。
4.Block Nested-Loop Join(块嵌套循环连接)
如果存在索引,那么会使用index的方式进行join,如果join的列没有索引,被驱动表要扫描的次数太多了。每次访问被驱动表,其表中的记录都会被加载到内存中,然后再从驱动表中取出一条与其匹配,匹配结束后清楚内存,然后再从驱动表中加载一条记录,然后把被驱动表的记录加载到内存匹配,这样周而复始,大大增加了IO的次数。为了减少被驱动表IO次数,就出现了BNLJ。
不再是逐条获取驱动表的数据,而是一块一块的获取,引入join buffer缓冲区, 将驱动表join 相关的部分数据列(大小受join buffer的限制)缓存到join buffer中,然后全表扫描被驱动表,被驱动表的每一条记录一次性和join buffer中所有的驱动表记录进行匹配(内存中操作)。将简单嵌套循环中的多次比较合并成一次,降低了备驱动表的访问频率。
注意:
这里缓存的不只是关联表的列,select后面的列也会缓存起来。
在一个有N个join关联的sql中会分配N-1个join buffer。所以查询的时候尽量减少不必要的字段,可以让join buffer中可以存放更多的列。

 

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的特点决定的。

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢编程的夏先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值