MySQL系列6 - join语句的优化

当被驱动表是一张非常大的冷表,且没有命中索引时。我们该如何做优化呢?

表t2的c2字段是没有索引的,且t2表是一张超级大的冷表,join语句如下:

select * from t1 straight_join t2 on (t1.c1=t2.c2);
其执行过程如下图:

图片

其中对t2表做多次全表扫描会带来什么问题呢?

innoDB存储引擎中有一片区域称为Buffer Pool,用来做查询缓存的。其使用的算法为LRU算法::第一次从磁盘读入内存的数据页,会先放在 old 区域。如果 1 秒之后这个数据页不再被访问了,就不会被移动到 LRU 链表头部。

但是由于t2表数据量比较大,每一次全表扫描t2表必然超过来1秒,那么再次全表扫描t2表时,冷表的数据页移到 LRU 链表头部。这样带来的后果是正常的业务查询数据无法进入链表头部。导致join语句开始执行到执行完成后的一定时间内链表头部的数据页没有被合理地淘汰。

所以,这种情况不仅这条语句本身执行效率慢,且同时影响了正常业务查询的效率。而且对正常业务查询的影响还是持续性的,得依靠后续的查询逻辑恢复Buffer Pool。

这种情况有两种优化方式:

增加join_buffer_size,减少t2表全表扫描的次数。

把t1表需要查询的字段读入内存临时表中。(起到一个大的join_buffer的作用)对应的执行语句如下:

// 创建临时表
create temporary table temp_t1(
id int primary key,
c1 int,
c2 int,
index(c1)
)engine=innodb;

// 插入t1数据
insert into temp_t1 select * from t1;

//使用临时表做join查询
select * from temp_t1 join t2 on (t2.c2=temp_t1.c1);

join查询时,条件是放在on里还是where里?

如下两条语句,你会做如何选择呢?

// sql1
select * from t1 left join t2 on t1.c1=t2.c1 where t2.c2>100;

//sql2
select * from t1 left join t2 on t1.c1=t2.c1 and t2.c2>100;
我们来看下sql1、sql2的执行过程:

图片

t1表读取一行L,到t2表根据on条件到t2表查找一行数据R(找不到填充null)

L与R的结果存放到临时结果集

读取t1表下一行

重复1、2、3步,直到t1表扫描完成

在临时结果集中根据where条件过滤得到结果集

sql1和sql2区别在于第五步,where条件的执行在on条件查询的结果后,所以尽量把可能多的条件放在on后,减少where的执行。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 1

图片看不到

1个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!