驱动表由WHERE过滤后结果集大小决定,而非物理行数;orders表500万行但过滤后仅800行,即为事实“小表”,应作驱动表,且被驱动表JOIN字段必须有索引。

驱动表不是由“物理大小”决定,而是由过滤后结果集大小决定
很多人一看到“小表驱动大表”,就去 SELECT COUNT(*) 数原始行数,结果选错了驱动表。真正该比的,是加了 WHERE 条件后实际参与 JOIN 的行数。
比如 orders 表有 500 万行,但加了 WHERE status = 'paid' AND created_at > '2026-06-01' 后只剩 800 行;而 users 表只有 10 万行,却没有任何过滤——此时 orders 才是事实上的“小表”,应作驱动表。
- 用
EXPLAIN看rows列:值更小的那张表,大概率被优化器选为驱动表 - 如果
rows值明显失真(比如显示 100 万但实际只返回几十行),说明统计信息过期,要执行ANALYZE TABLE orders - 别信表名或注释里写的“维度表=小表”,
product_category表可能因历史数据膨胀到千万级
被驱动表的 JOIN 字段必须有索引,否则小表驱动毫无意义
小表只循环 1000 次,但如果每次都要全表扫描大表 100 万行,总扫描量仍是 10 亿行——和大表驱动没区别。关键在被驱动表的连接字段是否有可用索引。
- 检查索引是否覆盖
ON条件列:比如JOIN orders o ON o.user_id = u.id,则orders.user_id必须有索引 - 避免隐式转换:
user_id是INT,但ON o.user_id = '123'会触发类型转换,导致索引失效 - 复合索引要注意最左匹配:
INDEX(status, created_at)对WHERE status = ?有效,但对WHERE created_at > ?无效
不同 JOIN 类型下,驱动表不是你想换就能换
INNER JOIN 的表顺序对优化器有强暗示作用,但 LEFT JOIN 和 RIGHT JOIN 的驱动方向是固定的,强行调换顺序可能改变语义或直接报错。
-
LEFT JOIN a ON b:a固定为驱动表,b固定为被驱动表;想让小表驱动,就得把小表放左边 -
RIGHT JOIN同理,小表必须放右边 -
STRAIGHT_JOIN可强制顺序,但仅当EXPLAIN明确显示优化器选错且你确认更优时才用,否则可能干扰后续优化器决策 - 开启
batched_key_access=on后,BKA 算法会批量请求被驱动表的主键,对驱动表大小敏感度下降,此时优先保证索引和过滤条件质量
真正影响性能的不是“谁放前面”,而是每次驱动行访问多少数据页
看 EXPLAIN 里的 key_len、ref 和 Handler_read_next 等状态值,比盯着表行数有用得多。一个 10 万行的表,如果每行要回表 3 次、读取 5 个数据页,实际开销可能远超一个带覆盖索引的百万行表。
-
key_len值偏小,说明索引没充分利用(比如只用了前缀) -
ref显示const或具体值是好信号;如果是func或NULL,说明连接条件不可预测 - 运行查询后查
SHOW STATUS LIKE 'Handler_read%',Handler_read_next过高意味着大量顺序扫描,Handler_read_rnd过高说明随机 I/O 严重


















