MySQL优化器动态选择驱动表,依据统计信息、索引等而非固定小表优先;EXPLAIN中rows小、type为ref/range且无Using join buffer的表才是驱动表。

MySQL 并不“总是”先读取小表作为驱动表——优化器会根据统计信息、索引可用性、连接条件写法等动态决策,小表只是它倾向选择的常见结果,而非铁律。
EXPLAIN 里哪张表是驱动表,得看 rows 和 type
驱动表的关键特征不是“写在 FROM 后面的第一个”,而是执行计划中 rows 值小、type 是 ref 或 range(说明走了索引)、且没出现 Using join buffer 的那张表。被驱动表往往 type=ALL、rows 很大,甚至带 Using join buffer (Block Nested Loop)。
常见误判点:
- LEFT JOIN 中左表固定为驱动表,哪怕它有 1000 万行 —— 此时“小表放左边”才是硬要求
- WHERE 条件没下推到驱动表上(比如写成
WHERE b.status = 'active'却把b放右边),优化器可能误判结果集大小 - 表的 cardinality 统计过期:
ANALYZE TABLE t1才能刷新行数估算,否则优化器按旧数据选错表
被驱动表没索引时,“小表驱动”反而可能失效
当被驱动表的 ON 字段没有索引,MySQL 会退化到 Block Nested-Loop Join(BNLJ)或 Hash Join(8.0.20+),此时性能瓶颈从“外层循环次数”转移到“join_buffer 大小”和“被驱动表全表扫描次数”。这时:
- 驱动表大小影响的是
join_buffer能缓存多少行 —— 字段越少、行越小,缓冲区利用率越高 - 增大
join_buffer_size比换驱动表更直接有效(但注意:该参数是会话级,且不能超过max_allowed_packet) - 如果被驱动表字段极少(比如只有 id + status),哪怕它是大表,用它当驱动表也可能比小表更省内存
STRAIGHT_JOIN 不是银弹,加错顺序会让查询更慢
STRAIGHT_JOIN 强制左边表为驱动表,但它只在优化器真选错了时才值得用。滥用后果明显:
- 把没 WHERE 过滤的大表放左边(如
STRAIGHT_JOIN orders o JOIN users u),等于主动触发千万次内层查找 - 和
FORCE INDEX冲突:驱动表用了索引,但被驱动表 ON 字段没索引,依然全表扫 - 视图或预编译语句里硬编码后,表数据分布变化(比如用户表突然暴涨 10 倍),原来快的顺序立刻变慢
真正该优先做的,是确保被驱动表的 ON 字段有高效索引(最好是主键或唯一索引),并检查是否因隐式类型转换(如 ON a.id = b.user_id 但 a.id 是 INT、b.user_id 是 VARCHAR)导致索引失效。
驱动表选对只是表象,底层决定快慢的是“每次驱动行要访问多少数据页”——这个数字藏在 EXPLAIN 的 key_len、ref 和实际执行时的 Handler_read_next 状态里。别只盯着表行数看。



















