MySQL 内连接、左连接、右连接是什么及底层实现
大家好,我是程序员花卷,现在是一名大三学生,在持续学习进步,在这里分享一些我的学习感悟和积累
在之前字节的面试中,面试官问了我一个问题是如果让我去设计MySQL 内连接、左连接、右连接你会怎么去设计这个算法,这个问题我直接懵了,现在我就来了解一下这个的底层。
我们来看一下mysql自己是怎么实现的
MySQL 连接的底层算法一共 3 种:Nested-Loop Join(嵌套循环连接 NLJ)、Block Nested-Loop Join(块嵌套循环 BNL)、Hash Join(哈希连接,8.0.18 + 支持)。 优化器会根据表大小、索引,自动选算法。
一、先理清概念:逻辑执行顺序(SQL 层面)
假设有表 A(左表)、表 B(右表)
- 先求笛卡尔积:A 每一行 × B 每一行,得到所有组合(临时大结果集)
- 应用 ON 连接条件:过滤掉不满足
A.id = B.a_id的行 - 连接类型处理(内 / 左 / 右)
- INNER JOIN 内连接:保留 ON 条件匹配成功的行;匹配失败的直接丢弃
- LEFT JOIN 左连接:保留左表 A 所有行;ON 匹配不到的,右表字段全部填 NULL
- RIGHT JOIN 右连接:保留右表 B 所有行;ON 匹配不到的,左表字段全部填 NULL
⚠️ 注意:这是逻辑模型,MySQL 底层不会傻乎乎先算出完整笛卡尔积(数据量大直接爆炸),优化器会用上面 3 种连接算法减少实际扫描行数。
二、三种连接底层算法
1. Nested-Loop Join 嵌套循环连接 NLJ(有索引时优先选)
适用:被驱动表连接字段有索引。 概念:分为驱动表(外层循环)、被驱动表(内层循环) 驱动表:小表,先扫描;被驱动表:大表,利用索引快速匹配。
伪代码:
for each row in 驱动表:
用这行的连接字段,去被驱动表索引查找匹配行
把匹配行组合,放入结果集
例子:A LEFT JOIN B ON A.id = B.a_id
- 左连接:驱动表一定是左表 A(左表必须全部输出,不能被过滤)
- 拿 A 每一行的 id,去 B 的 a_id 索引查找匹配记录
- 找到:组合 A+B 行
- 找不到:A 行保留,B 字段补 NULL
内连接 INNER JOIN:优化器可以自由选小表当驱动表,不一定是写在前面的表,优化器会选行数更少的表做驱动,提升性能。
NLJ 优势:利用索引,不需要创建临时大集合; 缺点:被驱动表无索引就不能用。
2. Block Nested-Loop Join 块嵌套循环 BNL(无索引时)
适用:被驱动表连接字段没有可用索引 直接单行循环太慢,所以引入 join buffer(连接缓冲区) 原理:一次性读取驱动表多行放到 join buffer 内存块,再拿这一批数据,去扫描一遍被驱动表,批量匹配,减少对被驱动表的扫描次数。
伪代码:
把驱动表一批行加载进 join buffer
for each row in 被驱动表:
和 buffer 里所有驱动表行做匹配,满足ON条件则组合结果
没有索引的时候只能全表扫描,BNL 通过批量减少扫描次数。 注意:BNL 依然会产生笛卡尔积的计算逻辑,只是分批在内存做,不会一次性生成完整笛卡尔积落盘。
3. Hash Join 哈希连接(MySQL8.0.18 之后,替代 BNL,无索引大数据)
适用:两表都没有索引,大表关联,OLAP 大查询场景。 原理:
- 选小表作为构建表 (build 表),扫描小表,基于连接 key 构建哈希表,放内存;内存放不下会溢写到临时磁盘文件
- 扫描大表(探测表 probe 表),逐行计算 key 哈希,去哈希表匹配
- 匹配成功的行合并输出
内连接、左 / 右连接都支持 Hash Join; 优势:大数据无索引场景性能远优于 BNL; 限制:MySQL8.0.18+,不支持
using/on里有范围条件。
三、内连接 / 左连接 / 右连接 在底层算法上的差异
1. INNER JOIN 内连接
逻辑:只保留 ON 匹配成功记录。 底层特点:
- 优化器自由选择驱动表(优先选行数少的)
- 匹配不到的行直接丢弃,不会补 NULL
- 三种算法都支持
2. LEFT JOIN 左连接
逻辑:左表全部保留,右表匹配不到填 NULL。 底层特点:
- 驱动表固定是左表,优化器不能调换!(核心考点)
- 只能用左表驱动右表
- ON 条件匹配失败,右表字段置 NULL
❗坑:
WHERE里写B.col = xxx会把左连接退化成内连接!因为 WHERE 会过滤 NULL 行。
3. RIGHT JOIN 右连接
逻辑:右表全部保留,左表匹配不到填 NULL。 底层特点:
- 驱动表固定是右表
- MySQL 优化器内部处理:会自动把 RIGHT JOIN 转成 LEFT JOIN 执行!
底层没有单独为右连接实现一套算法,优化器改写 SQL:A RIGHT JOIN B → B LEFT JOIN A,然后按左连接逻辑执行。所以开发中一般不推荐写 RIGHT JOIN,可读性差。
四、笛卡尔积底层怎么产生?
笛卡尔积定义:A 表 M 行,B 表 N 行,笛卡尔积 = M×N 行,所有行组合。
- 逻辑层面:多表连接最原始的模型就是笛卡尔积,然后用 ON 过滤。
- 物理执行层面:
- 如果没有 ON 条件
SELECT * FROM A JOIN B:MySQL 会真实计算完整笛卡尔积。NLJ/BNL/Hash Join 都会遍历产生全部组合。数据量稍微大就直接爆结果集。 - 如果有 ON 条件:数据库不会先生成全量笛卡尔积再过滤!(很多人面试踩坑)。优化器用 NLJ/BNL/Hash Join,边匹配边过滤,提前剪掉不满足条件的组合,避免生成巨大中间结果。
- 如果没有 ON 条件
举例子:A (1000 行) LEFT JOIN B (1000 行) ON A.id=B.a_id 笛卡尔积是 100 万行,但有索引 NLJ 只需要 1000 次索引查找,实际只访问很少行,不会生成 100 万临时数据。
我是后端新人花卷,持续学习中,如果你觉得文章有帮助,请帮忙转发给更多的好友,感谢大家的阅读。