JOIN 慢的时候,不能只看表的数量。真正决定成本的是连接顺序、驱动表行数、被驱动表有没有合适索引,以及每一轮连接要做多少次查找。
MySQL 执行 JOIN,本质上是在多张表之间组织中间结果。驱动表选错、被驱动表无法点查、过滤条件下推不充分,都会把一次连接放大成成倍扫描。
先把机制边界说清楚
JOIN 的本质是把多张表按照连接条件组合成中间结果。MySQL 会选择一个连接顺序,从驱动表取一批行,再去被驱动表查匹配行。被驱动表如果有合适索引,成本接近多次点查;没有索引,就可能退化成成倍扫描。
整体路径
上面这张图先看粗线条:宏观上,JOIN 链路分成三层:优化器先根据统计信息决定表顺序和访问路径;执行器按嵌套循环、批量访问或哈希连接组织数据;存储引擎负责每次索引查找、范围扫描或全表扫描。连接慢,通常是这三层中的某一层估错或缺少支撑结构。
底层流程
底层拆解先看数据结构。「JOIN 算法」至少涉及下面几类结构:
- 驱动表:连接循环的外层输入,行数越小越容易控制总成本。
- 被驱动表索引:决定每一轮连接是点查、范围查还是扫描。
- join buffer:无法有效用索引时,用来缓存外层行并减少重复扫描。
- 哈希表:MySQL 8.0 特定场景下用于无索引等值连接的内存结构。
再看完整执行流程:
- 优化器枚举可能的连接顺序。
- 根据统计信息估算每张表访问成本和中间结果规模。
- 选择驱动表,并从外层表读取候选行。
- 对每一批外层行访问被驱动表,使用索引、join buffer 或哈希连接。
- 生成中间结果,继续连接下一张表或返回。
取舍与边界
版本差异上,MySQL 长期以 nested-loop join 为核心。MySQL 8.0 引入并逐步增强 hash join,8.4 文档中已经明确默认会在可行场景尽量使用 hash join;但在典型 OLTP 等值连接里,最重要的仍然是让被驱动表连接列有可用索引。
JOIN 的短板是错误会被乘法放大。驱动表多扫 1 万行,被驱动表每行再扫 1 千行,问题立刻变成千万级访问。统计信息失真、关联条件缺索引、返回列过宽、连接后再过滤,都会让中间结果膨胀。
典型问题:用机制化例子排查
JOIN 成本可以按乘法放大来看:驱动表多一批行,被驱动表缺一个索引,每一轮连接都会把成本往上推。小数据量没问题,不代表路径是稳定的。
可以落到这些动作:
- 先看连接顺序和每张表 rows,定位乘法从哪里开始放大。
- 确保被驱动表连接列有索引,并和过滤条件组成合适联合索引。
- 减少连接前输入行数,不要把过滤条件都放到连接后才处理。
- 大结果集分析型 JOIN 和 OLTP 核心查询分开治理,必要时用汇总表或搜索引擎承接。
收束:JOIN 优化是控制乘法
JOIN 优化不是少写 JOIN,而是控制连接乘法。只要驱动表够小、被驱动表访问路径稳定,多表连接可以很稳;反过来,一处缺索引就能把成本放大。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

