join优化

  • 建表语句
-- 示例表:
CREATE TABLE `t1` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `a` int(11) DEFAULT NULL,
 `b` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `idx_a` (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 create table t2 like t1;

 -- 插入一些示例数据
-- 往t1表插入1万行记录
drop procedure if exists insert_t1;
delimiter ;;
create procedure insert_t1()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into t1(a,b) values(i,i);
set i=i+1;
end while;
end;;
delimiter ;

call insert_t1();


drop procedure if exists insert_t2;
 delimiter ;;
 create procedure insert_t2()
 begin
 declare i int;
 set i=1;
 while(i<=1000)do
 insert into t2(a,b) values(i,i);
 set i=i+1;
 end while;
 end;;
 delimiter ;
 call insert_t2();
  • 关键字解释

驱动表与被驱动表:关联查询时,按照执行的先后顺序区分(explain查看,先执行的就是驱动表,执行计划结果的id如果一样则按从上到下顺序执行sql),先执行的成为驱动表,后执行的成为被驱动表。

当使用left join时,左表是驱动表,右表是被驱动表,当使用right join时,右表时驱动表,左表是被驱动表;当使用join时,mysql会选择数据量比较小的表作为驱动表,大表作为被驱动表

大表与小表:在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与 join 的各个字段的总数据量,数据量小的那个表,就是“小表”,应该作为驱动表

mysql的表关联常见有两种算法

Nested-Loop Join(NLJ) 算法、Block Nested-Loop Join(BNL) 算法

  • 嵌套循环连接 Nested-Loop Join(NLJ) 算法

EXPLAIN select * from t1 inner join t2 on t1.a= t2.a; 

上面sql的大致流程如下:
1. 从表 t2 中读取一行数据(执行计划结果的id如果一样则按从上到下顺序执行sql如果t2表有查询过滤条件的,会从过滤结果里取出一行数据);
2. 从第 1 步的数据中,取出关联字段 a,到表 t1 中查找;
3. 取出表 t1 中满足条件的行,跟 t2 中获取到的结果合并,作为结果返回给客户端;
4. 重复上面 3 步。
整个过程会读取 t2 表的所有数据(扫描1000行),然后遍历这每行数据中字段 a 的值,根据 t2 表中 a 的值索引扫描 t1 表中的对应行 ( 扫描1000次 t1 表的索引,1次扫描可以认为最终只扫描 t1 表一行完整数据,也就是总共 t1 表也扫描了1000行 )。因此整个过程扫描了 2000 行。

  • 基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法

把驱动表的数据读入到 join_buffer 中,然后扫描被驱动表,把被驱动表每一行取出来跟 join_buffer 中的数据做对比。

EXPLAIN select * from t1 inner join t2 on t1.b= t2.b;

 上面sql的大致流程如下:
1. 把 t2 的所有数据放入到 join_buffer 中
2. 把表 t1 中每一行取出来,跟 join_buffer 中的数据做对比
3. 返回满足 join 条件的数据

整个过程对t1和t2都做了一次全表扫描,扫描总次数= t1表行数+ t2表行数 = 100000 + 1000 = 101000;由于join_buffer中的数据都是无序的,所以每次针对t1表中的每一行,都要做100000次判断,总判断次数 = t2表行数 * t1表行数 = 100000000。

如果t2表中数据量比较大,join_buffer一次放不下,就会采用分批读取的方式进行。这样就会多读(扫描)一次t2表。

被驱动表的关联字段没索引为什么要选择使用 BNL 算法而不使用 Nested-Loop Join 呢?

如果上面第二条sql使用 Nested-Loop Join,那么扫描行数为 100 * 10000 = 100万次,这个是磁盘扫描。很显然,用BNL磁盘扫描次数少很多,相比于磁盘扫描---IO,内存计算会快得多
因此MySQL对于被驱动表的关联字段没索引的关联查询,一般都会使用 BNL 算法。如果有索引一般选择 NLJ 算法,有索引的情况下 NLJ 算法比 BNL算法性能更高。

总结区分标准:1、一般 join 语句中,如果执行计划 Extra 中未出现 Using join buffer 则表示使用的 join 算法是 NLJ;否则则代表使用了BNL算法;2、如果关联字段使用了索引,一般是NLJ算法。

对于关联sql的优化

  1. 关联字段加索引,让mysql做join操作时尽量选择NLJ算法
  2. 小表驱动大表,写多表连接sql时如果明确知道哪张表是小表可以用straight_join写法固定连接驱动方式,省去mysql优化器自己判断的时间

大小表优化拓展

in和exsits优化,原则:小表驱动大表,即小的数据集驱动大的数据集

  • in

select * from A where id in (select id from B)

#等价于:
 for(select id from B){
 select * from A where A.id = B.id
 }

  • exists:当A表的数据集小于B表的数据集时,exists优于in

将主查询A的数据,放到子查询B中做条件验证,根据验证结果(true或false)来决定主查询的数据是否保留

select * from A where exists (select 1 from B where B.id = A.id)
 #等价于:
 for(select * from A){
 select * from B where B.id = A.id
 }

 #A表与B表的ID字段应建立索引

对于小表定义的明确

使用explain查查看join的执行计划;

主表和字表条件存放的位置

没有通过注解放到sprng容器中的时候怎么使用

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值