MySQL Join的使用
一、使用join语句的优势?
- 使用join语句,性能比强行拆成多个单表执行SQL语句的性能要好;
- 使用join语句的话,需要让
小表做驱动表
;
前提是,“可使用被驱动表的索引”
二、怎么选择驱动表?
在join语句执行过程中,驱动表是走全表扫面,而被驱动表是走树搜索(需建立索引);
使用小表做驱动表;
三、什么是MRR?
Multi-Range-Read 优化(MRR),这个优化的主要目的是尽量使用顺序读盘
;
因为大多数的数据都是按照主键递增顺序插入得到的,所以我们可以认为,如果按照主键的递增顺序查询的话,对磁盘的读比较接近顺序读,能够提升读性能;
假设,执行此语句,MRR优化的设计思路,执行流程:
select * from t1 where a>=1 and a<=100;
- 根据索引 a,定位到满足条件的记录,将id值放入read_rnd_buffer中;
- 将read_rnd_buffer 中的id进行递增排序;
- 排序后的id数组,依次到主键id索引中查记录,并作为结果返回;
read_rnd_buffer的大小是由read_rnd_buffer_size参数控制的,若步骤 1 read_rnd_buffer存满了,就会先执行完步骤2、3,然后情况read_rnd_buffer,之后继续招索引a的下个记录,并继续循环;
稳定使用MRR优化的话,需要设置set optimizer_switch=“mrr_cost_based=off”
文档说,是现在的优化器策略,判断消耗的时候,会更倾向于不使用MRR,把mrr_cose_based设置为off,就是固定使用MRR了
总结:
MRR提升性能的核心:能够再索引 a 上做范围查询,得到足够多的主键,完成排序后再回表,体现出顺序性的优势。
四、优化:
1.NLJ优化:
从驱动表t1,一行行的取出 a 的值,再到被驱动表t2去join,此时没有利用到MRR的优势
;
如果可以使用
Index Nested-Loop join
算法,应该选择小表做驱动表,可以用被驱动表上的索引,其实是没问题的;
2.BKA优化:
Batched Key Access
,是MySQL 5.6引入的对Index Nested-Loop Join
(NLJ
)的优化。
- BKA优化的思路,复用了
join_buffer
- 在BNL算法中,利用了
join_buffer
来暂存驱动表的数据,在NLJ里面并没有利用到join_buffer
- 在
join_buffer
中放入的数据为P1~P100,表示只会取查询所需要的字段;
如果
join_buffer
放不下P1~P100,就会将这100行数据分成多段执行
启用:
BKA算法依赖于MRR
SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
3.BNL优化:
- 可能会多次扫描被驱动表,占用磁盘IO资源;
- 判断Join条件需要执行M∗NM∗N次对比,如果是大表会占用非常多的CPU资源;
- 可能会导致Buffer Pool的热数据被淘汰和正常的业务数据无法成为热数据,进而影响内存命中率;
注意:如果优化器选择了BNL
算法,就需要做优化:
- 给被驱动表Join字段加索引,把
BNL
算法转换成BKA
算法; - 临时表;
五、怎么优化join语句?
NLJ、BNL的优化方法:
- BKA算法是对NLJ算法的优化,一次取出一批数据的字段到join_buffer中,然后批量join;
- BKA算法依赖于MRR,因为批量join找到被驱动表的非聚集索引字段通过MRR去查找行数据;
- BKA优化是MySQL已经内置支持的,建议默认使用;
- BNL算法效率低,建议都尽量转成BKA算法,优化的方向就是给被驱动表的关联字段加上索引;
参考资料:
《MySQL 实战45讲》