#======================================================##
MySQL关联查询算法:
BNL(Block Nested-Loop)
ICP(Index Condition Pushdown)
MRR(Multi-Range Read)
BKA(Batched Key Access)算法
#======================================================##
BNL(Block Nested-Loop)
场景:
假设TB1和TB2进行关联查询,以TB1为外表循环扫描每行数据到TB2中查找匹配的记录行,但因为TB2中没有可使用的索引,须要扫描整个T2表的数据,所以外层TB1的数据行数决定内层TB2的扫描次数。服务器
优化:
将外层表TB1的数据行进行拆分N个Block,每一个Block中包含M条数据,对TB2进行N次扫描,在扫描TB2数据的每一行时将其与一个Block的数据进行匹配,将原来对TB2表的扫描次数从M*N次下降到N次。oop
重点:
一、内表没有可利用的索引
二、内表和外表的顺序不能对换,如LEFT JOIN操做性能
该算法在MySQL 5.1版本中便已存在。优化
#======================================================##
ICP(Index Condition Pushdown)
场景:
假设表TB1上有索引IDX_C1_C2_C3(C1,C2,C3),对于查询SELECT * FROM TB1 WHERE C1='XXX' AND C3='XXX'blog
在MySQL 5.6版本之前,因为缺乏C2的过滤条件,Innodb存储引擎层只能使用索引IDX_C1_C2_C3按照C1='XXX'条件找出全部知足条件的索引记录,再根据这些索引记录去汇集索引中查找,将找到的表数据返回给MySQL Server层,而后由MySQL Server层使用C3='XXX'条件进行过滤获得最终结果。排序
再MySQL 5.6版本中引入ICP特性,Innodb存储引擎层只能使用索引IDX_C1_C2_C3按照C1='XXX'条件去扫描全部知足条件的索引记录,再将这些索引记录按照C3='XXX'条件进行过滤,并按照过滤后的索引记录去去汇集索引中查找,将找到的表数据返回给MySQL Server层,获得最终结果。索引
假设知足C1='XXX'条件的数据行为100000条,而知足C1='XXX' AND C3='XXX'的数据行为100条,则:
一、在MySQL 5.5版本中,须要对TB1的汇集索引进行100000次Index Seek操做,Innodb存储引擎层向MySQL Server层传递100000行数据。
二、在MySQL 5.6版本中,使用ICP仅须要对TB1的汇集索引进行100次的Index Seek操做,Innodb存储引擎层向MySQL Server层传递100行数据。it
ICP经过将过滤条件由MySQL Server层"下沉"到存储引擎层,从而达到:
一、减小对汇集索引查找的操做次数;
二、减小从存储引擎层返回给MySQL Server层的数据量;
三、减小MySQL Server层访问存储引擎层的次数。io
PS1: ICP仅使用于非汇集索引。
PS2: 在MySQL 5.6中仅支持普通表进行ICP操做,而MySQL 5.7中支持对分区表进行ICP操做。
#======================================================##
MRR(Multi-Range Read)
假设表TB1上有索引IDX_C1(C1),对于查询SELECT * FROM TB1 WHERE C1 IN('XXX1','XXX2',....,'XXXN')
在MySQL 5.6版本之前,先按照C1='XXX1'条件对IDX_C1进行索引查找,再按照找到的索引记录去TB1的汇集索引中找到对应数据记录,再按照C1='XXX2'...到C1='XXXN'进行操做,将每次操做的结果集合并获得最终结果集。因为根据C1条件获得的索引记录中的包含的汇集键数据时无序的,致使对汇集索引的Index seek操做形成较多的随机IO,影响服务器存储性能。
在MySQL 5.6版本中引入MRR特性,先按照C1='XXX1'....和C1='XXXM'的条件找到知足条件的索引记录放到buffer中,当Buffer满时再将buffer中的索引记录按照汇集键进行排序,按照排序后的结果去汇集索引中找到相应记录,经过排序,能够有效地将原来的随机查找改成顺序查找,将部分随机IO转换为顺序IO,提示查询性能,下降查询对服务器IO性能的消耗。
PS1: MRR也仅适用于非汇集索引,且根据非汇集索引获得的结果集在汇集键上是随机无序的。
PS2: 假设上面TB1的汇集索引为ID,那么IDX_C1(C1)等价于IDX_C1(C1,ID),若是仅对非汇集索引进行单个等值查询,那么获得的结果集对汇集键也是有序的,无需使用MRR特性。
PS3: MRR中涉及到的Buffer的大小取决于参数read_rnd_buffer_size的设置
#======================================================##
BKA(Batched Key Access)
场景:
假设TB1和TB2进行关联查询,以TB1为外表循环到TB2中进行关联匹配,表TB2上有可以使用的索引。
在MySQL 5.6版本前,只能循环TB1中的数据依次到TB2上进行索引查找,若是TB1上的数据是无序的,则对TB2的索引查找也是随机的,产生大量的随机IO操做。
在MySQL 5.6版本中,按照MRR的特性,先将TB1中的数据放入Buffer中,当Buffer满时对Buffer中的数据按照关联键进行排序,而后有序地对TB2进行索引查找,将部分随机IO操做转换为顺序IO操做。
PS1: BKA以来于MRR,所以要使用BKA必须开启MRR特性,但又因为基于mrr_cost_based的成本估算不能保证MRR被使用,所以官方推荐关闭mrr_cost_based。
PS2: BKA使用的Buffer的大小取决于参数join buffer size
#======================================================##
设置开启MRR和BKA并关闭mrr_cost_based
SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
#======================================================##
BKA和BNL的区别:
一、内表索引,BKA要求内表有可使用的索引,而BNL则是由于内表没有可以使用的索引而不得已的优化
二、算法目的,BKA算法的目的是减小对内表的随机Index Seek操做和下降随机IO,而BNL算法的目的是减小对内表的扫描次数和减小扫描带来的IO开销。
#======================================================##