explain中rows突增表明join导致中间结果集失控,主因是过滤条件未下推至on、右表字段放where、关联字段无索引或类型不一致、on中使用函数及驱动表选择不当。

EXPLAIN 里 rows 突然暴涨,说明中间结果失控
JOIN 慢最直观的信号不是查询超时,而是 EXPLAIN 输出中某一行的 rows 值远高于预期——比如左表 1 万行,JOIN 后变成 200 万行。这说明关联过程没剪枝,大量无效行被拉进后续计算。
常见原因包括:
- 时间、状态等关键过滤字段只写在最外层
WHERE,没下推到ON子句或子查询里 -
LEFT JOIN的右表过滤条件(如u.status = 'active')错放在WHERE,导致先全量 JOIN 再丢弃,优化器无法提前跳过不匹配行 - 关联字段存在大量重复值或 NULL,例如
order_items.order_id有 5 条重复记录,JOIN 时会生成 5 倍膨胀行
type = ALL 或 index,代表右表根本没走索引
EXPLAIN 中 type 列出现 ALL(全表扫描)或 index(全索引扫描),基本等于宣告该表 JOIN 阶段没用上有效索引。
排查要点:
- 确认关联字段两侧类型严格一致:比如左表是
VARCHAR(20),右表不能是VARCHAR(50),否则可能触发隐式转换,索引失效 - 检查是否在
ON里用了函数,例如ON UPPER(a.code) = UPPER(b.code),两边索引全作废 - 复合索引要满足最左前缀原则:如果
ON a.x = b.x AND a.y = b.y,而 b 表只有(y, x)索引,x无法单独走索引
OR 条件写在 ON 里,直接触发全表扫描
ON b.x = a.x OR b.y = a.y 这种写法,MySQL 几乎必然放弃索引,退化为嵌套循环+全表扫描,性能断崖下跌。
正确解法不是加 hint,而是拆成 UNION ALL:
- 确保
b.x和b.y各自有单列索引(或复合索引以它们为前导列) - 两个子查询分别从左表出发,
LEFT JOIN b AS b1 ON ...和LEFT JOIN b AS b2 ON ... -
SELECT字段顺序、类型、NULL 性必须完全一致,否则UNION ALL报错 - 绝不加
DISTINCT——除非业务真要排重,否则它会强制排序去重,开销远大于UNION ALL
驱动表选错,嵌套循环复杂度爆炸
MySQL 默认用嵌套循环(Nested Loop),外层驱动表每行都要去内层表匹配一次。如果驱动表是千万级大表,哪怕内层每次只花 0.1ms,总耗时也轻松破百秒。
优化方向很实际:
- INNER JOIN 通常由优化器自动选小表当驱动表,但前提是统计信息准确——定期执行
ANALYZE TABLE - LEFT JOIN 的驱动表固定为左表,无法切换,所以左表必须先过滤:用子查询或 CTE 提前裁剪,比如
FROM (SELECT * FROM orders WHERE dt >= '2026-08-01') o LEFT JOIN ... - 避免在存储过程中把多表 JOIN 塞进一个大查询里,先聚合再 JOIN,能把中间数据量压到千分之一
真正卡住性能的,往往不是 JOIN 语法本身,而是你没意识到哪一行 EXPLAIN 的 rows 已经翻了 20 倍,或者没发现 ON 里那个 UPPER() 让索引彻底失效。










