inner join执行顺序由优化器决定而非书写顺序,先生成中间结果集再连接后续表;驱动表应小且过滤性强,被驱动表on字段必须有高效索引,否则引发全表扫描级联。

INNER JOIN 的执行顺序由驱动表决定
MySQL 的 INNER JOIN 不是“同时连三张表”,而是按左到右依次连接:先算 table1 INNER JOIN table2 得到中间结果集,再拿这个结果集去连 table3。这个过程中,第一个表(最左边)默认是驱动表,它会被全表扫描;后续每张表都是被驱动表,靠索引快速匹配。
常见错误现象:EXPLAIN 显示 type: ALL 出现在大表上,且 rows 高达百万——说明它被当成了驱动表。
- 驱动表行数越少,中间结果集越小,后续 JOIN 的压力越低
- 即使 SQL 写成
A JOIN B JOIN C,优化器也可能重排顺序,但前提是所有 ON 字段都有索引、统计信息准确 - 用
EXPLAIN FORMAT=TREE才能看到真实执行顺序,别信书写顺序
ON 条件字段没索引,小表也救不了大表
哪怕你把小表放在最左边当驱动表,只要被驱动表的 ON 字段(比如 orders.user_id)没索引,MySQL 就只能对被驱动表全表扫描——这时驱动表再小也没用,总扫描行数 = 驱动表行数 × 被驱动表行数。
性能影响:从几百毫秒跳到几秒甚至超时,EXPLAIN 里会看到 type: ALL 和极高的 rows 值。
-
orders.user_id必须单独建索引,不能只依赖INDEX(user_id, created_at)——除非查询里也用到了created_at - 如果 JOIN 后还要
ORDER BY created_at LIMIT 20,优先建INDEX(user_id, created_at),让一个索引覆盖 JOIN + 排序 - 避免在 ON 中用函数,如
ON DATE(o.created_at) = u.register_date,这会让索引完全失效
WHERE 条件放错位置,等于白建索引
把过滤条件写在 WHERE 而不是 ON,可能导致索引无法下推。例如 orders.status = 'paid' 放在 WHERE,MySQL 会先完成整个 users INNER JOIN orders,再过滤;而放在 ON 子句里(即 ON o.user_id = u.id AND o.status = 'paid'),优化器更可能利用 status 索引提前剪枝。
容易踩的坑:多表 JOIN 时,外层 WHERE 可能意外排除掉本该由后续表补上的数据。比如查“活跃用户及其最近订单”,若把 o.created_at > '2024-01-01' 放最外层 WHERE,那些只在 2023 年下单的用户就彻底消失了。
- 状态类、时间范围等高选择性条件,优先塞进对应表的
ON子句 - 确认业务语义:是否真需要保留无匹配记录的行?不需要就别用
LEFT JOIN,改INNER JOIN更可控 - 测试时先跑不带
WHERE的 JOIN,用EXPLAIN看key和rows是否合理,再加条件对比
STRAIGHT_JOIN 不是银弹,乱用反而锁死优化器
STRAIGHT_JOIN 强制 MySQL 按 SQL 书写顺序执行 JOIN,绕过优化器决策。它只在你通过 EXPLAIN 明确发现优化器选错了驱动表,且已验证手动指定顺序确实更快时才值得用。
为什么慎用:一旦统计信息更新、数据分布变化或新增索引,原来最优的顺序可能变差,而 STRAIGHT_JOIN 会继续硬扛旧路径,导致性能突然恶化。
- 不要为“看起来顺”而加
STRAIGHT_JOIN,它不是可读性优化手段 - 加了之后必须定期复查
EXPLAIN,尤其在大表数据量增长后 - 优先修复索引和统计信息,而不是靠
STRAIGHT_JOIN掩盖问题
真正卡住性能的,往往不是 JOIN 本身,而是驱动表选错 + 被驱动表缺索引 + 过滤条件没下推这三者叠加。调优时盯紧 EXPLAIN 里的 type、key、rows 和 Extra 四列,比反复改写 SQL 顺序更有效。











