连表操作时:先根据查询条件和查询字段确定驱动表,确定驱动表之后就可以开始连表操作了,然后再在缓存结果中根据查询条件找符合条件的数据
1、mysql连表中的驱动表如何选择?
在对最终结果集没影响的前提下,优先选择结果集最小的那张表作为驱动表。
2、mysql查询表的时候的预估结果集如何计算?
每行查询字节数 * 预估的行数 = 预估结果集
3、通过where预估结果行数,遵循哪些规则
如果where里没有相应表的筛选条件,无论on里是否有相关条件,默认为全表
如果where里有筛选条件,但是不能使用索引来筛选,那么默认为全表
如果where里有筛选条件,而且可以使用索引,那么会根据索引来预估返回的记录行数
4、a和c数据表如何,查询select a.*,c.* from a join c on a.a2=c.c2 where a.a1>5 and c.c1>5; 为何是以字段数多的c表做驱动表?
用a做驱动表需要回查:因为通过已知的c.c2无法得到c.*,需要根据c表的主键再次回查c表
如果select的字段是a.*,c.c2的话就可以直接用a表做驱动表,此时不需要回查c表
如果用a作为驱动表,通过索引c2关联到c表,那么还需要再回表查询一次,因为仅仅通过c2获取不到c.*的数据,还需要通过c2上的主键c1再查询一次。而上一个sql查询的是c2,不需要额外查询。同时因为a表只有两个字段,通过a2索引能够直接获得a.*,不需要额外查询。
5、两表关联查询的内在逻辑是怎样的?
嵌套循环连接:mysql表与表之间的关联查询使用Nested-Loop join算法,顾名思义就是嵌套循环连接
在使用索引关联的情况下,有Index Nested-Loop join和Batched Key Access join两种算法;
在未使用索引关联的情况下,有Simple Nested-Loop join和Block Nested-Loop join两种算法;
mysql表与表之间的关联查询使用Nested-Loop
join算法,顾名思义就是嵌套循环连接,但是根据场景不同可能有不同的变种:比如Index
Nested-Loop join,Simple
Nested-Loop join,Block Nested-Loop join, Betched Key Access join等。
6、驱动表一般怎么选(带索引)(人为的直观)?
where条件里的表:一般在where条件里面的那个表就是驱动表,where条件里面我们一般找的索引,所以会导致(每行查询字节数 * 预估的行数 = 预估结果集)公式里面的预估的行数非常小
7、连表(连接表的外键有索引)操作的具体操作步骤是怎样的(循环操作)?
1、从驱动表中取出一条符合要求的数据
2、回表:通过这条数据然后通过索引找到关联表中的数据(如果只需要where条件里面的字段就不需要这步回表操作)
3、数据加入缓存
8、连表操作的具体操作步骤给我们的启示是什么?
不仅主键使用索引,外键肯定一定要使用索引,where条件里面的那些字段最好都用上索引
9 两表关联查询的内在逻辑是怎样的?
mysql表与表之间的关联查询使用Nested-Loop join算法,顾名思义就是嵌套循环连接,但是根据场景不同可能有不同的变种:比如Index Nested-Loop join,Simple Nested-Loop join,Block Nested-Loop join, Betched Key Access join等。
·在使用索引关联的情况下,有Index Nested-Loop join和Batched Key Access join两种算法;
·在未使用索引关联的情况下,有Simple Nested-Loop join和Block Nested-Loop join两种算法;
我们先来看有索引的情形,sql如下:
select a.*,c.*
from a join c on a.a2=c.c2
where a.a1>4;
首先根据第一步的逻辑来确定驱动表a,然后通过a.a1>4,a.来查询一条记录a1=5,将此记录的c2关联到c表,取得c2索引上的主键c1,然后用c1的值再去聚集索引上查询c.*,组成一条完整的结果,放入net buffer,然后再根据条件a.a1>4,a. 取下一条记录,循环此过程。过程图如下:

通过索引关联被驱动表,使用的是Index Nested-Loop join算法,不会使用msyql的join buffer。根据驱动表的筛选条件逐条地和被驱动表的索引做关联,每关联到一条符合的记录,放入net-buffer中,然后继续关联。此缓存区由net_buffer_length参数控制,最小4k,最大16M,默认是1M。如果net-buffer满了,将其发送给client,清空net-buffer,继续上一过程。
通过上述流程知道,驱动表的每条记录在关联被驱动表时,如果需要用到索引不包含的数据时,就需要回表一次,去聚集索引上查询记录,这是一个随机查询的过程。每条记录就是一次随机查询,性能不是非常高。mysql对这种情况有选择的做了优化,将这种随机查询转换为顺序查询,执行过程如下图:

此时会使用Batched Key Access join 算法,顾名思义,就是批量的key访问连接。
逐条的根据where条件查询驱动表,将符合记录的数据行放入join buffer,然后根据关联的索引获取被驱动表的索引记录,存入read_rnd_buffer。join buffer和read_rnd_buffer都有大小限制,无论哪个到达上限都会停止此批次的数据处理,等处理完清空数据再执行下一批次。也就是驱动表符合条件的数据可能不能够一次处理完,而要分批次处理。
当达到批次上限后,对read_rnd_buffer里的被驱动表的索引按主键做递增排序,这样在回表查询时就能够做到近似顺序查询:

如上图,左边是未排序前的随机查询示意图,右边是排序后使用MRR(Multi-Range Read)的顺序查询示意图。
因为mysql的InnoDB引擎的数据是按聚集索引来排列的,当对非聚集索引按照主键来排序后,再用主键去查询就使得随机查询变为顺序查询,而计算机的顺序查询有预读机制,在读取一页数据时,会向后额外多读取最多1M数据。此时顺序读取就能排上用场。
BKA算法在需要对被驱动表回表的情况下能够优化执行逻辑,如果不需要回表,那么自然不需要BKA算法。
如果要使用 BKA 优化算法的话,你需要在执行 SQL 语句之前先设置:
set optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
前两个参数的作用是要启用 MRR(Multi-Range Read)。这么做的原因是,BKA 算法的优化需要依赖于MRR,官方文档的说法,是现在的优化器策略,判断消耗的时候,会更倾向于不使用 MRR,把 mrr_cost_based 设置为 off,就是固定使用 MRR 了。)
3 .多表连接如何执行?是先两表连接的结果集然后关联第三张表,还是一条记录贯穿全局?
其实看连接算法的名称:Nested-Loop join,嵌套循环连接,就知道是多表嵌套的循环连接,而不是先两表关联得出结果,然后再依次关联的形式,其形式类似于下面这样:
for row1 in table1 filtered by where
{
for row2 in table2 associated by table1.index1 filtered by where
{
for row3 in table3 associated by table2.index2 filtered by where
{
put into net-buffer then send to client;
}
}
}
对于不同的join方式,有下列情况:
Index Nested-Loop join:
sql如下:
select a.*,b.*,c.*
from a join c on a.a2=c.c2
join b on c.c2=b.b2
where b.b1>4;
通过explain查看其执行计划:

执行前mysql执行器会确定好各个表的关联顺序。首先通过where条件,筛选驱动表b的第一条记录b5,然后将用此记录的关联字段b2与第二张表a的索引a2做关联,通过Btree定位索引位置,匹配的索引可能不止一条。当匹配上一条,查看where里是否有a2的过滤条件且条件是否需要索引之外的数据,如果要则回表,用a2索引上的主键去查询数据,然后做判断。通过则用join后的信息再用同样的方式来关联表c。

Block Nested-Loop join 和 Batched Key Access join : 这两个关联算法和Index Nested-Loop join算法类似,不过因为他们能使用join buffer,所以他们可以每次从驱动表筛选一批数据,而不是一条。同时每个join关键字就对应着一个join buffer,也就是驱动表和第二张表用一个join buffer,得到的块结果集与第三个表用一个join buffer。





