驱动表选错会导致被驱动表索引失效:即使on字段有索引,若驱动表过大或无有效过滤,优化器会跳过索引直接全表扫描,explain中type=all、key=null或出现using join buffer即为典型表现。

驱动表选错,索引根本没机会被用上
MySQL 的 JOIN 执行依赖驱动表(outer table)和被驱动表(inner table)的配合。优化器决定哪个表当驱动表,直接影响被驱动表是否能走索引。如果驱动表选得太大、或者没有合适索引,被驱动表即使有索引也常被跳过——不是索引坏了,是压根没轮到它出场。
EXPLAIN 中 type=ALL 或 key=NULL 通常就是驱动表惹的祸
看 EXPLAIN 结果时,重点关注被驱动表那一行:type 是 ALL、key 是 NULL、Extra 出现 Using join buffer (Block Nested Loop),基本可以断定:被驱动表没走索引,而是被暴力扫描了。
- 典型场景:小表没索引,却被当成驱动表,大表被迫全扫
- 更隐蔽的情况:两个表都有索引,但驱动表返回 10 万行,被驱动表每行都得回表匹配,优化器干脆放弃索引,改用哈希连接或 BNL
- 注意:即使
ON条件字段本身有索引,若驱动表结果集过大,MySQL 也可能判定“走索引成本 > 全表扫描”,主动弃用
如何让优化器选对驱动表?
不能只靠 STRAIGHT_JOIN 强制,关键在数据分布和索引设计:
- 确保驱动表的
WHERE条件能高效过滤(比如带高区分度索引),让它输出尽可能少的行数 - 被驱动表的
ON字段必须有索引,且类型与驱动表完全一致(避免隐式转换) - 联合索引要覆盖
ON+WHERE字段,例如JOIN orders o ON o.user_id = u.id WHERE o.status = 'paid',索引应建在(user_id, status)而非单列user_id - 用
ANALYZE TABLE更新统计信息,否则优化器可能误判行数,选错驱动表
为什么有时候加了索引还是慢?
因为索引生效的前提是它被“选中参与执行”。驱动表选错时,哪怕被驱动表每个字段都建了索引,也会被优化器直接忽略——它连进 B+ 树查找的第一步都没走到。真正卡点往往不在索引有没有,而在谁先动、动多少。











