MySQL系列6 - join语句的优化

Posted 栋总侃技术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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


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

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

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

// 创建临时表create temporary table temp_t1(id int primary keyc1 intc2 intindex(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里?

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

// sql1select * from t1 left join t2 on t1.c1=t2.c1 where t2.c2>100;
//sql2select * from t1 left join t2 on t1.c1=t2.c1 and t2.c2>100;

我们来看下sql1、sql2的执行过程:

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

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

  3. 读取t1表下一行

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

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


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


往期回顾:







以上是关于MySQL系列6 - join语句的优化的主要内容,如果未能解决你的问题,请参考以下文章

Mysql优化系列之——优化器对子查询的处理

MySQL性能优化:EXPLAIN 执行计划与join

MySQL性能优化:EXPLAIN 执行计划与join

Day899.Join语句优化 -MySQL实战

mysql语句优化

MySQL45讲join语句优化