当前位置: 首页 > news >正文

MySQL JOIN 机制与多表查询优化:驱动表选择、连接算法与执行计划解析

文章目录

  • 《MySQL JOIN 机制与多表查询优化:驱动表选择、连接算法与执行计划解析》
    • 一、前言:为什么 JOIN 会变慢?
    • 二、JOIN 的底层执行算法:Nested Loop Join(NLJ)
      • **① Nested Loop Join(嵌套循环,NLJ)**
      • **✔ 实际执行:先拿驱动表,再 JOIN 被驱动表**
    • 三、MySQL 如何选择驱动表?
      • 判断驱动表的核心原则:
    • 四、EXPLAIN 如何判断驱动表?
      • **EXPLAIN 中同 id 时,出现顺序表示驱动顺序。**
    • 五、JOIN 的三种常见类型与优化建议
      • **① INNER JOIN(内连接)**
      • **② LEFT JOIN(左连接)**
      • **③ RIGHT JOIN(右连接)**
    • 六、多表 JOIN(3 张表以上)如何确定顺序?
      • **多表 JOIN 优化原则:**
    • 七、JOIN 常见的性能杀手(重点)
      • 1. JOIN 字段未建索引
      • 2. WHERE 条件写在错误的位置
      • 3. JOIN 字段⽤了函数
      • 4. 多张大表 JOIN
    • 八、JOIN 优化的黄金法则
      • **✔ 法则 1:小表驱动大表**
      • **✔ 法则 2:JOIN 字段必须建索引**
      • **✔ 法则 3:WHERE 应尽量放在 JOIN 之前执行**
      • **✔ 法则 4:避免 JOIN 字段上使用函数和类型转换**
      • **✔ 法则 5:SELECT 只取需要的列**
      • **✔ 法则 6:必要时将大表拆分或分库分表**
    • 九、面试高频问题与答题模板
    • 十、总结


《MySQL JOIN 机制与多表查询优化:驱动表选择、连接算法与执行计划解析》


一、前言:为什么 JOIN 会变慢?

大家好,我是程序员卷卷狗。

JOIN 是业务中最常见的 SQL 操作之一,
但也是最容易写“慢”的 SQL。

常见的性能瓶颈有:

  • 驱动表选错导致扫描量激增;
  • JOIN 条件未命中索引 → 全表扫描;
  • 结果集过大 → 触发临时表、文件排序;
  • 多表 JOIN 顺序不合理 → 优化器误判。

JOIN 优化的核心本质就是一句话:

减少参与 JOIN 的数据量。


二、JOIN 的底层执行算法:Nested Loop Join(NLJ)

MySQL 只实现了一种 JOIN 算法:

① Nested Loop Join(嵌套循环,NLJ)

伪代码如下:

for 被驱动表的每一行 (outer)for 驱动表中满足条件的行 (inner)输出两表匹配的行

但实际执行顺序与概念相反:

✔ 实际执行:先拿驱动表,再 JOIN 被驱动表

官方定义:

  • 驱动表(Driving Table):最先读取的表
  • 被驱动表(Driven Table):根据驱动表结果继续匹配的表

图示:

驱动表 records1  ×  被驱动表 records2
-------------------------------------
最终 JOIN = records1 * records2 的匹配集合

为了保证性能:

驱动表必须尽可能小,被驱动表必须命中索引。


三、MySQL 如何选择驱动表?

MySQL 的驱动表选择完全依赖优化器(Optimizer)。

判断驱动表的核心原则:

原则解释
1. WHERE 过滤后数据量最小的表优先越小的表做驱动表,循环次数越少
2. ON 条件能命中索引的表优先作为被驱动表JOIN 时必须命中索引
3. EXPLAIN 中 id 值大者先执行id 越大,越先执行(驱动表)
4. type 趋向 ref、const 的表优先避免 range、ALL

举例:

SELECT * 
FROM user u 
JOIN order o ON u.id = o.user_id
WHERE u.age > 18;

驱动表:user(u)
被驱动表:order(o)

因为:

  • user 先过滤 age > 18 → 数据量小;
  • order.user_id 上有索引 → 可直接匹配。

四、EXPLAIN 如何判断驱动表?

示例:

EXPLAIN
SELECT * FROM user u
JOIN orders o ON u.id = o.user_id;

可能输出(简化版):

idtabletypekey
1userALLNULL
1ordersrefidx_user_id

解释:

  • 两行 id 都是 1 → 同一层 JOIN
  • 顺序:上面的先执行 → user 为驱动表
  • 下表 orders 用 ref 索引匹配 → 被驱动表

记住:

EXPLAIN 中同 id 时,出现顺序表示驱动顺序。


五、JOIN 的三种常见类型与优化建议

① INNER JOIN(内连接)

SELECT * FROM A JOIN B ON A.id=B.id

优化:

  • JOIN 条件字段必须有索引
  • 小表驱动大表
  • WHERE 先过滤大数据量的表

② LEFT JOIN(左连接)

SELECT * FROM A LEFT JOIN B ON A.id=B.id

注意:A 永远是驱动表,不能被优化器调整。

优化:

  • B.id 必须有索引
  • WHERE 条件避免过滤 B(避免转 INNER JOIN)
  • 避免在 B 表 JOIN 字段使用函数

③ RIGHT JOIN(右连接)

不建议使用 RIGHT JOIN,
因为它完全等价于 LEFT JOIN 翻转顺序,
但会降低可读性。


六、多表 JOIN(3 张表以上)如何确定顺序?

举例:

A JOIN B ON A.id = B.a_id
JOIN C ON B.id = C.b_id

执行顺序通常是:

1. A JOIN B
2. (A+B) JOIN C

优化器会根据过滤条件、索引、统计信息决定每一步的驱动与被驱动表。

多表 JOIN 优化原则:

原则解释
1. 永远让过滤后行数最少的表靠前减少循环次数
2. 每一步 JOIN 的 ON 字段必须命中索引否则全表扫描
3. 避免在 JOIN 字段上使用函数或类型转换避免索引失效
*4. 避免使用 SELECT ,减少网络与磁盘开销提升吞吐
5. 多表 JOIN 尽量少于 3 张表否则成本极高

七、JOIN 常见的性能杀手(重点)

1. JOIN 字段未建索引

SELECT * FROM user u JOIN order o ON u.id=o.user_id;

o.user_id 无索引 → 全表扫描。


2. WHERE 条件写在错误的位置

SELECT * 
FROM user u 
LEFT JOIN orders o ON u.id=o.user_id
WHERE o.user_id IS NOT NULL;

→ LEFT JOIN 被转成 INNER JOIN。


3. JOIN 字段⽤了函数

ON DATE(u.create_time) = DATE(o.create_time)

索引全部失效。


4. 多张大表 JOIN

JOIN 是 N² 的复杂度,
表越大,损耗呈指数级增长。


八、JOIN 优化的黄金法则

✔ 法则 1:小表驱动大表

减少嵌套循环的迭代次数。


✔ 法则 2:JOIN 字段必须建索引

尤其是被驱动表的 JOIN 字段。


✔ 法则 3:WHERE 应尽量放在 JOIN 之前执行

过滤越早越好。


✔ 法则 4:避免 JOIN 字段上使用函数和类型转换

必须让索引“裸奔”。


✔ 法则 5:SELECT 只取需要的列

减少数据传输量。


✔ 法则 6:必要时将大表拆分或分库分表

降低单表数据量。


九、面试高频问题与答题模板

问题答案要点
Q1:MySQL JOIN 底层使用什么算法?Nested Loop Join(嵌套循环)。
Q2:什么是驱动表?怎么选?最先读取的表,通常是过滤后较小的表。
Q3:JOIN 为什么会变慢?扫描量大、未命中索引、临时表或文件排序。
Q4:如何优化多表 JOIN?小表驱动大表、JOIN 字段建索引、避免函数。
Q5:LEFT JOIN 中谁是驱动表?左表(永远是驱动表)。
Q6:如何通过 EXPLAIN 判断驱动表?看 id 相同的多行中出现顺序,越上者越先执行。
Q7:为什么 JOIN 字段必须建索引?提升匹配效率,否则每次匹配都全表扫描。

十、总结

JOIN 是 SQL 中最复杂也最容易写痛点的部分,
其性能完全取决于:

  • 驱动表选择
  • 索引命中
  • JOIN 条件写法
  • Where 过滤顺序
  • 表数据量

一句话记住:

小表驱动大表,被驱动表靠索引。

下一篇(第 21 篇),我将写——
《MySQL缓存机制与查询缓存的消亡史》
讲清楚优化器、执行器、存储引擎之间的协作与下推机制。

http://www.dtcms.com/a/606649.html

相关文章:

  • AI代码开发宝库系列:特征工程
  • WordPress全站广告巩义市建设局网站
  • 信誉好的顺德网站建设如何用百度平台营销
  • Apache Cloudberry 孵化报告(202508-202510)
  • 【2025 JAVA面试题】 常见几个具体问题
  • dedecms大气金融企业网站模板八年级信息所用软件做网站
  • C语言编译器手机| 如何选择适合的C语言编译器应用
  • Java 并发踩坑:高并发库存扣减丢失更新,从悲观锁到分布式锁的终极方案
  • 杭州建设网站职称人才工作专题wordpress 插件手机
  • lancedb create_scalar_index 创建索引
  • Python 网络编程
  • Java后端常用技术选型 |(四)微服务篇
  • Vue3 + Vite项目=》babel转义兼容低版本实现+vite 分包处理方案
  • 【GIS入门】GeoTIFF栅格地理数据格式介绍和基础概念详解
  • 网站开发与设计模板百度seo网站排名
  • 校园网站建设总结flash网站系统
  • openlayer省市县json
  • 计算机视觉11-相机模型与多视几何
  • 网站开发公司 经营范围eclipse做网站表格
  • 建立网站 域名 服务器7一12岁手工简单又实用
  • kotlin 集成 unity
  • 麻辣烫配方教授网站怎么做前端做的好的网站
  • [Unity Shader Base] RayMarching in Cloud Rendering
  • Java后端常用技术选型 |(六)避坑手册
  • 教育网站平台建设对网站做维护
  • RUST实现简易随机密码生成器
  • 个人主页网站网站正在建设中源码
  • 网站邮箱配置百度seo哪家公司好
  • wordpress建站 购物网页设计实训报告不足
  • 蚂蚁与浙大签约,共建大数据认知计算联合研究中心