小表放前面能提升性能的本质是优化器倾向用小表作驱动表以减少嵌套循环外层迭代次数:外层10行仅触发10次内层访问,而100万行则达百万次,即便内层走索引,I/O与CPU开销也剧增。

为什么 JOIN 时小表放前面能提升性能
本质不是“语法要求小表在前”,而是优化器更倾向用小表做驱动表来减少嵌套循环的外层迭代次数。MySQL 的 JOIN 默认使用 Nested-Loop Join(NLJ),外层表每行都要去内层表全量扫描或索引查找一次。如果外层是 10 行的小表,最多执行 10 次内层访问;换成 100 万行的大表作外层,就是百万次——哪怕内层走索引,I/O 和 CPU 开销也指数级上升。
MySQL 优化器是否真会自动选小表当驱动表
会,但有前提:表统计信息准确、连接条件上有可用索引、没有强制 hint 干扰。你可以用 EXPLAIN 看 type 和 rows 列判断哪张表被当作驱动表:
-
rows值明显小的那张表,通常是驱动表 - 若
type是ALL或index且rows很大,说明它大概率是被驱动表(即内层) - 用
STRAIGHT_JOIN可强制指定顺序,但仅在优化器选错且你确认小表更适合驱动时才用
小表驱动大表在不同 JOIN 类型下的适用性
这个经验主要适用于 INNER JOIN 和未启用 MRR/BKA 的场景。其他情况需注意:
-
LEFT JOIN:左表固定为驱动表,无法交换顺序,此时“左表尽量小”才是关键 -
RIGHT JOIN:同理,右表固定驱动 - 开启
batched_key_access=on后,优化器可能改用 BKA 算法,对驱动表大小敏感度下降 - 如果大表连接字段有唯一索引,而小表没有,有时大表驱动反而更快(因单次查找成本极低)
真正该关注的不是“谁放前面”,而是这三件事
写 SQL 时盯着 FROM a JOIN b 谁小谁大,不如检查以下三点:
- 连接字段是否都有高效索引(尤其是被驱动表的 ON 条件列)
- 表的
cardinality统计是否过期?用ANALYZE TABLE更新 - 是否存在隐式类型转换或函数包裹导致索引失效,让本该走索引的驱动行为退化成全表扫描
驱动表大小只是表象,底层决定性能的是“每次驱动行要访问多少数据页”——这个数字藏在 EXPLAIN 的 key_len、ref 和实际执行时的 Handler_read_* 状态里。别只看表行数。











