explain 显示 type=all 或 rows 很大说明发生全表扫描,多表 join 时任一表未走索引或驱动表选择不当均会导致性能退化;需检查 on/where 字段索引、字符集与类型一致性、避免函数/隐式转换,并合理使用 straight_join 和 analyze table。

为什么 EXPLAIN 显示 type=ALL 或 rows 特别大
这通常意味着 MySQL 没有走索引,正在对某张表做全表扫描。多表 JOIN 时,哪怕只有一张表没走索引,整个执行计划就可能退化——尤其当驱动表选错、或 ON 条件字段缺失索引时。
检查方式:对每个 JOIN 的 ON 字段、以及 WHERE 中的过滤字段,单独确认是否建了合适的索引。注意复合索引顺序必须匹配查询条件的使用顺序。
-
LEFT JOIN t2 ON t1.id = t2.t1_id→t2.t1_id必须有索引(最好是(t1_id)或作为前导列的复合索引) - 如果写成
ON t1.name = t2.category,而t1.name和t2.category都没索引,必然触发全表扫描 - 字符集不一致也会让索引失效,比如
t1.name是utf8mb4,t2.category是utf8,即使都有索引,JOIN 也无法用上
如何控制驱动表顺序(STRAIGHT_JOIN 的实际用法)
MySQL 优化器有时会把小表当被驱动表、大表当驱动表,导致嵌套循环次数爆炸。你不能靠 LEFT JOIN 的书写顺序来保证驱动顺序——优化器可能重排。真正可控的方式是显式指定:
SELECT /*+ STRAIGHT_JOIN */ ... FROM big_table t1 JOIN medium_table t2 ON t1.id = t2.t1_id JOIN small_table t3 ON t2.id = t3.t2_id;
加 /*+ STRAIGHT_JOIN */ 提示后,MySQL 就严格按 FROM 和 JOIN 的顺序执行,t1 → t2 → t3。前提是你要自己评估好各表的行数、筛选后结果集大小。
- 用
EXPLAIN FORMAT=TREE看执行树结构,确认驱动方向是否符合预期 - 避免在高并发 OLTP 场景滥用
STRAIGHT_JOIN,它绕过优化器决策,一旦数据分布变化(比如某表突然膨胀),性能可能断崖下跌 - 临时表或派生表(subquery in FROM)默认无法被
STRAIGHT_JOIN控制,得先物化成 WITH 或中间表
JOIN 条件中函数或类型转换导致索引失效
这是最隐蔽也最常踩的坑:看着 SQL 很干净,但执行计划里却出现 type=ALL。典型例子是日期字段用了函数:
ON DATE(t1.create_time) = DATE(t2.event_date)
哪怕 t1.create_time 和 t2.event_date 都有索引,这个 DATE() 也会让索引完全失效。同理,隐式类型转换也一样:
ON t1.user_id = t2.uid -- t1.user_id 是 BIGINT,t2.uid 是 VARCHAR
MySQL 会把 t1.user_id 全部转成字符串去比,索引自然作废。
- 改写为范围条件代替函数:用
t1.create_time BETWEEN ? AND ?替代DATE(t1.create_time) = ... - 确保 JOIN 字段类型和长度完全一致,包括是否为
NOT NULL;可用SHOW CREATE TABLE对比确认 - 用
CONVERT()或CAST()强制统一类型不如直接改表结构,后者一劳永逸
临时表和排序导致 Using temporary / Using filesort
多表 JOIN 后再 GROUP BY 或 ORDER BY 非驱动表字段,很容易触发临时表和文件排序——尤其是当结果集超过 tmp_table_size 或 max_heap_table_size 时,会落地到磁盘,性能骤降。
关键判断点:看 EXPLAIN 的 Extra 列是否含 Using temporary 或 Using filesort。不是所有情况都能避免,但可以收敛影响面:
- 优先在驱动表的关联字段上做
ORDER BY,例如驱动表是t1,就只按t1.id或t1.created_at排序 - 如果必须按被驱动表字段排序,考虑把该字段冗余到驱动表(如把
t2.status同步到t1),用空间换时间 -
SQL_BIG_RESULT提示可强制优化器用磁盘临时表,避免内存爆掉,但仅适用于已知结果集巨大且排序不可避免的场景
复杂 JOIN 的执行计划容易受统计信息陈旧影响,记得定期跑 ANALYZE TABLE,特别是大表数据批量导入后。否则优化器基于错误的行数估算,选错驱动表或访问路径,比没索引还难排查。










