你们知道 EXPLAIN 是否能看出 MySQL 的 JOIN 关联顺序? 结论是:完全可以 ,而且 EXPLAIN 是查看、分析 MySQL JOIN 关联顺序的核心、最常用手段,没有之一。
一、核心结论:EXPLAIN 中 JOIN 关联顺序的核心规则
✅ 规则1:EXPLAIN 结果的行展示顺序 = MySQL 实际执行的 JOIN 关联顺序
EXPLAIN 执行后会返回一个结果集(多行数据),MySQL 是「从下往上」执行 JOIN 关联的,这个顺序是绝对的核心,记住这个口诀:
EXPLAIN 结果越靠后的行 ,是 MySQL 执行 JOIN 时先访问的表(驱动表);
EXPLAIN 结果越靠前的行 ,是 MySQL 执行 JOIN 时后访问的表(被驱动表)。
✅ 规则2:驱动表 & 被驱动表的定义(理解关联顺序的前提)
- 驱动表 (driving table) :JOIN 关联时先被加载、先被扫描的表,MySQL 会用驱动表的每条数据,去匹配另一张表的数据;
- 被驱动表 (driven table) :JOIN 关联时后被加载、被匹配的表,也叫「匹配表」;
- 驱动表的选择,是 MySQL 优化器的核心逻辑之一,小表(数据量少、过滤后结果集小)优先作为驱动表,这是最优的 JOIN 执行策略。
二、最直观案例:一眼看懂 JOIN 关联顺序
案例表准备
有两张常见业务表,做 JOIN 查询:
sql
-- 订单表(数据量较大,假设10万条)
CREATE TABLE `order` (
id INT PRIMARY KEY,
user_id INT,
order_no VARCHAR(32),
KEY idx_user_id (user_id)
);
-- 用户表(数据量较小,假设1万条)
CREATE TABLE `user` (
id INT PRIMARY KEY,
name VARCHAR(32),
age INT
);
执行 JOIN 查询+EXPLAIN
sql
EXPLAIN SELECT o.*, u.name FROM `order` o LEFT JOIN `user` u ON o.user_id = u.id;
EXPLAIN 结果(核心关注 table 列)
假设 EXPLAIN 返回 2行结果 ,table 列展示如下:
| id | select_type | table | type | ... |
|---|---|---|---|---|
| 1 | SIMPLE | o | ALL | ... |
| 1 | SIMPLE | u | ref | ... |
关联顺序解读
EXPLAIN 结果里,行1是表 o(order)、行2是表 u(user),根据「从下往上执行」的规则:
✅ MySQL 实际执行顺序:先查 user 表(驱动表) → 再查 order 表(被驱动表)
补充:哪怕你写的是 order LEFT JOIN user,MySQL 优化器会自动调整顺序,因为 user 是小表,做驱动表效率更高!
三、EXPLAIN 中判断 JOIN 的两个核心关键字段(必看)
EXPLAIN 的结果有很多列,判断是否是 JOIN 查询、以及 JOIN 关联的匹配方式,只需要重点看 2 个关键字段,缺一不可:
✅ 字段1:type 列 - 关联匹配的「访问类型」
type 列代表 MySQL 对表的访问/匹配方式 ,只要是 JOIN 查询,被驱动表的 type 字段一定会出现 ref / eq_ref 这两个值之一,这是 JOIN 的「身份标识」。
eq_ref:最优的 JOIN 匹配,一对一匹配(比如关联主键/唯一索引),性能极高;ref:非常好的 JOIN 匹配,多对一匹配(比如关联普通索引),性能也很好;
补充:如果 JOIN 的被驱动表 type 是 ALL,说明是「笛卡尔积匹配/全表扫描匹配」,是性能极差的 JOIN,必须优化(加索引)。
✅ 字段2:possible_keys/key 列 - JOIN 用的索引
possible_keys:MySQL 认为可以用来做 JOIN 关联的索引(候选索引);key:MySQL 最终实际选用的 JOIN 关联索引(核心字段);
关键优化点:如果 JOIN 查询的 key 列是 NULL,说明 MySQL 没有用到任何索引做关联,就是上面说的「全表扫描 JOIN」,一定要给 JOIN 的关联字段(如 o.user_id/u.id)加索引。
四、多表 JOIN 场景:3表/多表的关联顺序怎么看?
如果是 3 张及以上表的 JOIN,规则完全不变,还是遵循:
EXPLAIN 结果 从下往上 执行,后行 = 先行执行(驱动表),前行 = 后执行(被驱动表)
案例:3表 JOIN
sql
EXPLAIN SELECT o.*, u.name, g.goods_name
FROM `order` o
LEFT JOIN `user` u ON o.user_id = u.id
LEFT JOIN `goods` g ON o.goods_id = g.id;
EXPLAIN 结果(table列):3行
行1 → table: o
行2 → table: g
行3 → table: u
实际执行顺序
第一步 :先访问 u 表(最下行,驱动表1)
第二步 :用 u 表的数据关联访问 g 表(中间行,被驱动表1、驱动表2)
第三步 :用 u+g 关联的结果,关联访问 o 表(最上行,被驱动表2)
总结:不管多少表 JOIN,只需要把 EXPLAIN 的结果行倒序看,就是完整的执行顺序!
五、MySQL 会不会「自己调整 JOIN 关联顺序」?
✅ 重要结论:会的!而且是常态!
你在 SQL 语句中书写的 JOIN 表顺序 ,不等于 MySQL 实际执行的 JOIN 顺序!
核心原因:MySQL 有「JOIN 优化器(关联顺序优化)」
MySQL 的优化器会基于「成本估算」,自动调整 JOIN 的关联顺序,核心原则是:
优先选择「小表/过滤后结果集最小的表」作为驱动表
因为驱动表越小,需要循环匹配的次数越少,JOIN 的整体执行效率越高。
比如你写的是 大表 JOIN 小表,MySQL 优化器会自动调整为 小表 JOIN 大表,这也是为什么我们不用刻意纠结 SQL 中 JOIN 的书写顺序的原因。
六、如何强制 MySQL 按照「我写的顺序」执行 JOIN?
如果某些特殊场景下,你确定自己写的 JOIN 顺序比 MySQL 优化器选的更优,想要禁用优化器的关联顺序调整,强制按 SQL 书写顺序执行 JOIN,有 2 种可靠方法:
✅ 方法1:使用 STRAIGHT_JOIN 关键字(推荐,轻量)
STRAIGHT_JOIN 是 JOIN 的「强制顺序版」,功能和 JOIN 完全一致,唯一区别是:强制 MySQL 按照 SQL 中书写的表顺序执行 JOIN,不做任何调整。
语法:把 SQL 中的 JOIN 替换成 STRAIGHT_JOIN 即可。
sql
-- 强制:先查 order 表 → 再查 user 表,完全按书写顺序执行
EXPLAIN SELECT o.*, u.name FROM `order` o STRAIGHT_JOIN `user` u ON o.user_id = u.id;
✅ 方法2:关闭优化器的关联顺序优化(不推荐,全局生效)
通过设置 MySQL 会话参数关闭优化器,会影响所有查询,谨慎使用:
sql
-- 关闭关联顺序优化
SET optimizer_switch='join_cache_bka=off';
-- 执行你的 JOIN 查询
EXPLAIN SELECT o.*, u.name FROM `order` o JOIN `user` u ON o.user_id = u.id;
-- 用完建议恢复默认
SET optimizer_switch='join_cache_bka=on';
✨ 全文核心知识点总结(必记)
EXPLAIN可以精准查看 MySQL JOIN 的关联顺序,是核心分析工具;- JOIN 关联顺序核心规则:
EXPLAIN结果 从下往上执行,后行是驱动表(先执行),前行是被驱动表(后执行); - 判断 JOIN 的 2 个核心字段:
type列(必为ref/eq_ref)+key列(非 NULL 代表用到关联索引); - 多表 JOIN 规则不变,倒序看
EXPLAIN结果就是执行顺序; - MySQL 会自动调整 JOIN 顺序(小表优先做驱动表),无需手动调整书写顺序;
- 强制按书写顺序执行:用
STRAIGHT_JOIN替换JOIN即可。