explain第一行的表才是实际驱动表,而非left join左表;优化器依成本模型决定顺序,小结果集应为驱动表,右表on字段须建索引,where中误用右表条件会转inner join并破坏左表保全性。

EXPLAIN 第一行就是驱动表
执行 EXPLAIN 后,结果集中第一行的 table 列所对应的表,就是当前查询实际使用的驱动表。这个结论不依赖经验或猜测,是 MySQL 优化器最终决策的直接体现。
常见误区是以为 LEFT JOIN 左边的表“一定”是驱动表——它在语义上是主表,但优化器仍可能因统计信息或索引缺失而改选其他表(尤其在 MySQL 8.0+ 中)。所以别信直觉,只信 EXPLAIN 输出的第一行。
- 如果
type是ALL或index,说明该驱动表正在全表扫描,风险极高 - 如果
rows值远大于该表真实行数,大概率是统计信息过期,需运行ANALYZE TABLE -
Extra出现Using join buffer,说明被驱动表没走索引,正退化为 Block Nested-Loop 连接,性能已受损
INNER JOIN 驱动表由预估结果集大小决定
对 INNER JOIN,MySQL 优化器会估算每张表经过 WHERE 条件过滤后的行数,并选择预估结果集更小的那张表作为驱动表。注意:这个“小”不是物理行数小,而是**过滤后参与连接的行数少**。
例如:
SELECT * FROM orders o INNER JOIN users u ON o.user_id = u.id WHERE o.status = 'paid' AND u.is_vip = 1;
若 orders 表中 status = 'paid' 的记录只有 500 行,而 users 表中 is_vip = 1 的有 2 万行,则优化器大概率选 orders 作驱动表——哪怕它物理上比 users 大得多。
- 关键前提:相关字段必须有可用索引,否则优化器无法准确估算,容易误判
-
WHERE条件写在驱动表上才有效;若只写在被驱动表上,可能无法影响驱动表选择 - 用
FORCE INDEX可临时干预,但属于绕过优化器,仅限调试或紧急修复
LEFT JOIN 驱动表基本固定,但 ON 条件质量决定性能
LEFT JOIN 的驱动表默认是左表(FROM 后第一个表),这点在 MySQL 5.7 到 8.0 各版本中都稳定。但它不等于“安全”——驱动表选对了,不代表性能就好。
真正卡脖子的是:被驱动表(右表)能否用上索引完成关联。
- 确保
ON右侧字段(如u.id)在被驱动表上有索引,否则驱动表每扫一行,都要对被驱动表全表扫描一次 -
ON条件中的字段类型必须严格一致(比如都是BIGINT,不能一边是VARCHAR一边是INT),否则索引失效 - 不要把本该写在
WHERE的过滤条件塞进ON,尤其对被驱动表:比如ON u.id = o.user_id AND u.deleted = 0,会导致 LEFT JOIN 语义变形(u.deleted != 0的行也会被保留为 NULL)
为什么加了索引,驱动表还是选错了?
索引存在 ≠ 索引可用。驱动表误选常源于以下三个隐藏问题:
- 外键字段未建索引:比如
orders.user_id关联users.id,但orders.user_id上没索引,优化器无法高效反查,被迫放弃以users为驱动表的计划 - 统计信息陈旧:
INFORMATION_SCHEMA.STATISTICS中的行数预估严重偏离实际,可通过SHOW INDEX FROM table_name查看Cardinality是否合理 - 隐式类型转换:如
ON CAST(o.user_id AS CHAR) = u.user_code,即使两边都有索引,也会导致索引失效,优化器只能退回到全表扫描逻辑
最稳妥的做法,是每次怀疑驱动表异常时,先跑一遍 EXPLAIN FORMAT=JSON,重点看 query_block.nested_loop 结构和 rows_examined_per_scan 字段——那里藏着优化器真实的成本权衡。











