MySQL:如果用left join的话,左边的表一定是驱动表吗

一、前言

**  在日常开发过程中关于MySQL的优化方面,我们知道小表驱动大表原理。例如left join,放在左边的表作为驱动表。但是用left join的话,左边的表一定是驱动表吗,本文将通过案例分析给出详细分析。**

二、概念

**  在MySQL中,JOIN操作涉及到两个或多个表的连接,其中一个表被称为驱动表(也称为外表),另一个表或多个表被称为被驱动表(也称为内表或从表)。**

**  驱动表(外表)**:在JOIN操作中,首先被访问的表称为驱动表。

**  被驱动表(内表或从表)**:在JOIN操作中,与驱动表进行匹配的表称为被驱动表。

三、优化策略(小表驱动大表原理)

**  在MySQL的JOIN操作中,当涉及到两个或多个表的连接时,其中一个表被选为驱动表(小表),另一个表作为被驱动表(大表)。小表驱动大表的原理就是尽量让数据量较小的表作为驱动表,通过其数据去匹配大数据量的表,以减少循环匹配的次数,从而提高查询性能。**

示例:

假设我们有两个表A和B,其中A表有1000行数据,B表有100万行数据。如果以A表作为驱动表去连接B表,那么最多只需要循环1000次;而如果以B表作为驱动表去连接A表,则需要循环100万次。显然,选择A表作为驱动表将大大提高查询性能。

选择小表作为驱动表的好处:

减少循环次数:当驱动表的数据量较小时,循环的次数会相应减少,从而减少了整体的计算量。反之,如果大表作为驱动表,则需要多次循环遍历大表的数据去匹配小表,这将大大增加计算量。

利用索引:如果小表上有合适的索引,MySQL可以更快地定位到需要的数据行,进一步减少扫描的行数。同时,索引的使用也可以提高JOIN操作的效率。

优化器决策:MySQL的优化器会根据表的统计信息、索引情况、查询条件等因素来自动选择最佳的驱动表。优化器的目标是找到一种执行计划,使得查询的代价(如I/O操作、CPU时间等)最小。

四、案例分析

**  我们在编写SQL的时候,通常情况下是这样子的**

LEFT JOIN:在左连接中,左边的表通常作为驱动表。

RIGHT JOIN:在右连接中,右边的表作为驱动表。

INNER JOIN:对于内连接,MySQL会自动选择数据量较小的表作为驱动表。

这是通常情况下,下面我们准备一些测试数据:

1. 准备数据:

a1表

a2表

2.准备查询语句1和查询语句2

java 复制代码
语句1
select * from a1 left join a2 on(a1.f1=a2.f1) and (a1.f2=a2.f2); 

语句2
select* from a1 left join a2 on(a1.f1=a2.f1)where (a1.f2=a2.f2);

3. 查询语句1的执行结果

4. 查询语句2的执行结果

5. 语句1的EXPLAIN执行结果分析

从EXPLAIN执行计划中看的出来

驱动表是表a1,被驱动表是表a2。

由于表a2的f1字段上没有索引,所以使用的是 Block Nested Loop Join(简称 BNL) 算法。

BNL算法基本原理

将外层循环的结果集存入join buffer:在BNL算法中,外层循环(通常是较小的表或结果集)的行或结果集会被存储在一个称为join buffer的内存区域中。

内层循环与buffer中的记录做比较:内层循环(通常是较大的表或结果集)的每一行数据会与整个buffer中的记录进行比较,而不是像传统的Nested Loop Join(NLJ)算法那样,每次只与外层循环的一行进行比较。

结合算法与执行计划语句1的执行过程如下:

①. 把表a1的内容读入join_buffer 中

②. 顺序扫描表a2,对于每一行数据,判断条件(a1.f1=a2.f1) and (a1.f2=a2.f2)是否满足,满足条件的记录, 作为结果集的一行返回。如果语句中有 where 子句,需要先判断 where 部分满足条件后,再返回。

③. 表a2扫描完成后,对于没有被匹配的表a1的行,把剩余字段补上 NULL,再放入结果集中。

6. 语句2的EXPLAIN执行结果分析

从EXPLAIN执行计划中看的出来

以表a2为驱动表的。

语句2的执行过程

顺序扫描表a2,每一行用a2.f1到表a1中去查,匹配到记录后判断a1.f2=a2.f2 是否满足,满足条件的话就作为结果集的一部分返回。

为什么呢,我们执行一下show warnings;看看优化器是怎么做的。
语句1

语句2

**  在 MySQL 里,NULL 跟任何值执行等值判断和不等值判断的结果,都是 NULL。这里包括, select NULL = NULL 的结果,也是返回 NULL。**

因此,语句2里面 where a1.f2=a2.f2 就表示,查询结果里面不会包含 a2.f2是NULL的行,这样这个left join的语义就是"找到这两个表里面,f1、f2 对应相同的行。

对于表a1中存在,而表a2中匹配不到的行,就放弃。这样,这条语句虽然用的是 left join,但是语义跟 join 是一致的。

因此,优化器就把这条语句的 left join 改写成了 join,然后因为表 a1 的 f1 上有索引,就把表 a2 作为驱动表。

五、结论

1、即使我们在SQL语句中写成left join,执行过程还是有可能不是从左到右连接的。也就是说,使用left join时,左边的表不一定是驱动表。

2、如果需要left join的语义,就不能把被驱动表的字段放在where条件里面做等值判断或不等值判断,必须都写在on里面。

六、总结

通过对案例的分析,我们在写sql的时候,就有了优化方案:

1. 用小结果集驱动大结果集,减少外层循环的数据量

2. 如果小结果集和大结果集连接的列都是索引列,mysql在join时也会选择用小结果集驱动大结果集,因为索引查询的成本是比较固定的,这时候外层的循环越少,join的速度便越快。

3. 为匹配的条件增加索引:争取使用Index Nested-Loop Join,减少内层表的循环次数

4. 增大join buffer size的大小:当使用Block Nested-Loop Join时,一次缓存的数据越多,那么外层表循环的次数就越少,减少不必要的字段查询。

5. 当用到Block Nested-Loop Join时,字段越少,join buffer 所缓存的数据就越多,外层表的循环次数就越少。

相关推荐
惜鸟1 分钟前
Pydantic 的 BaseSettings 配置解析
后端·python
Java中文社群8 分钟前
Dify实战案例:MySQL查询助手!嘎嘎好用
java·人工智能·后端
程序猿阿伟11 分钟前
《深度探秘:Java构建Spark MLlib与TensorFlow Serving混合推理流水线》
java·spark-ml·tensorflow
TDengine (老段)20 分钟前
TDengine 开发指南—— UDF函数
java·大数据·数据库·物联网·数据分析·tdengine·涛思数据
键盘林23 分钟前
分布式系统简述
java·开发语言
可儿·四系桜24 分钟前
如何在 Java 中优雅地使用 Redisson 实现分布式锁
java·开发语言·分布式
bobz96530 分钟前
源码编译 libvirt
后端
道友小小怪31 分钟前
Spring Bean的生命周期及常见问题
后端·spring·面试
Java微观世界33 分钟前
让你秒懂字符集与编码:详解ASCII、GB2312、Unicode和UTF-8
后端
sszdzq39 分钟前
SpringCloudGateway 自定义局部过滤器
java·spring boot