刨根问底拦不住的MySQL系列之Join原理(一)

1,665 阅读4分钟

写在前面

那天午后,面试官直接壁咚我在会议室门口

拿着我的简历,上来就问我SQL调优

我说,来吧

我已经强大的不要理由

面试官问起Join

心开始颤抖

犹记得,DBA的新人手册开头

《SQL Join要怎么用》

时间很快,会议室的灯开了很久

面试官让我赶紧走,丝毫不对我挽留

还不忘暖心心的安慰我,看看八佾的文章,给自己加加油

正文

就这么说吧,现如今,谁的简历上没有一句精通MySQL原理及调优,都不要意思递这个简历。今天我们就从Join这个角度跟面试官来展示一下,我们是如何敢把精通二字写在简历开头的!

Join的流程

让我们先创建两张表t1t2

-- 创建表t1,对idx建立普通索引
create table t1
(
	id int auto_increment,
	idx int null,
	constraint t1_pk
		primary key (id)
);
create index t1__index_idx
	on t1 (idx);
-- 创建表t2,对idx建立普通索引	
create table t2
(
	id int auto_increment,
	idx int null,
	constraint t2_pk
		primary key (id)
);
create index t2__index_idx
	on t2 (idx);

现在执行SQL:

select * from t1 straight_join t2 on t1.idx = t2.idx;

straight_join : 是一种以固定方式的连接,强制使用左表作为驱动表。

这个SQL的执行流程是什么样的呢?

  1. 第一步:从表t1读出一行数据ROW
  2. 第二步:从数据行中取出字段idx到表t2中去查找
  3. 第三步:在t2中筛选出满足条件的行,跟t1读出的数据ROW组装,构成结果集的一行
  4. 重复步骤一、二、三步,直到t1全部查完

这种JOIN的方式,叫做Index Neste-Loop Join,通过名字我们大概可以知道这种Join的特点:

一是Nested-Loop嵌套查询,相当于双重for循环。

二是Index 在查询的过程中,我们是可以使用到被驱动表的索引的,被驱动表是树型查找,节约很多的扫描行数。

我们尝试分析一下,Index Neste-Loop Join的查询流程。

![Index Neste-Loop Join](/Users/huxingbo/Documents/elastic/Index Neste-Loop Join.png)

Join没走索引的场景

刚刚我们嘎嘎聊了,通过索引进行JOIN的流程,那如果被驱动表没有索引呢?直观上来说,根据Index Neste-Loop Join经验,我们可以仍然采用嵌套循环。遍历循环t1表的数据,去t2表全表扫描,符合条件的筛选出来,组装成数据集,这种JOIN方式,跟Index Neste-Loop Join方式很相似,同时,它也有个相似的名字——Simple Nested-Loop Join

但是,我们知道这种效率必然很慢,需要对两张表进行扫描,虽然整体流程没有办法改变,但是能否对其中的一些步骤做优化,是我们想要探索的。

实际上,由于扫描的行数没有改变,也可以时间复杂度上无法再优化,MySQL因此单独开辟了一个空间内存Join Buffer来把JOIN的操作放到了内存里操作。无索引的Join流程就变成了这样:

select * from t1 straight_join t2 on t1.idx = t2.idx;

第一步:把t1的查询字段,放入内存Join_Buffer,这个内存区域是线程独享的。

第二步:扫描表t2,把每一行取出来,跟Join_Buffer里面的数据进行匹配,封装结果集。

细心的你,可能会发现一个问题,虽然Join_Buffer是内存操作,那么这个内存得多大的,实际上来说是很小的,默认是256K,并且受join_bu!er_size控制大小。因此,肯定会存在放不下的情况,Mysql把数据拆分成段,分段放,因此这种连接方式被称为Block Nested-Loop Join

八股文

通过上面的分析,我们知道了JOIN的执行流程

在这里我想告诉你几个八股文:

  • 索引的JOIN可以使用,不走索引的Block,不建议使用。
  • 不管是什么JOIN,都建议使用小表作为驱动表

那么在优化中常常提到以小表作为驱动表也是这样的原因。但是我们怎么定义小表呢?

  • 严格来说,我们应该对各个表进行条件筛选,查询的数据字段的总数据量为判断标准

  • Join的优化不应当仅仅局限于索引,小表驱动表,我们还可以通过MRR(Multi-Range Read),5.6的(BKA)Batched Key Access算法优化JOIN

关于MRR和BKA到底是什么呢?我们下次再会?欢迎关注催更