left join的驱动表不一定是左表,mysql优化器基于成本模型(i/o与cpu代价)决定执行顺序,explain第一行所示表才是实际驱动表;小结果集(过滤后)应为驱动表,右表on字段须建合适索引,where中误用右表条件会转为inner join并破坏左表保全性。

LEFT JOIN 的驱动表不一定是左表
MySQL优化器会根据成本估算重排JOIN顺序,即使写了 LEFT JOIN,也可能把右表当作驱动表——只要它算出来这样更快。你看到的SQL写法只是语义约束(必须保留左表所有行),不是执行指令。EXPLAIN第一行显示的表才是实际驱动表,别凭肉眼判断。
大表当驱动表时性能灾难的具体表现
当右表被选为驱动表,而它又没走索引,就会触发全表扫描:左表每一条记录都要匹配右表全部数据。比如左表1万行、右表500万行,最坏情况要跑500亿次比较。常见诱因包括:
-
ON条件字段没索引,或类型不一致导致隐式转换(如a.id是BIGINT,b.ref_id是VARCHAR) -
WHERE里对右表字段过滤(如WHERE b.status = 'done'),让优化器误判为可转成INNER JOIN,进而放弃左表驱动 - 右表统计信息过期,优化器低估了它的实际行数
如何让小结果集成为实际驱动表
目标不是“左表必须驱动”,而是“真正小的结果集驱动”。实操上优先级如下:
- 把经过强过滤的表放
FROM后第一位,哪怕它是业务上的“从表”;例如加了WHERE log.created_at > '2024-01-01'的日志表,比用户表还小,就该放左边 - 用
STRAIGHT_JOIN强制顺序(仅限MySQL),但得确认左表确实小且右表索引可用,否则可能更慢 - 对右表
ON字段建复合索引,且最左前缀必须匹配连接字段;例如ON b.a_id = a.id AND b.status = 'active',索引得是(a_id, status),不能只建(status)
检查与验证必须做两件事
光改SQL不够,必须验证执行计划是否真按你预期走:
- 用
EXPLAIN FORMAT=TREE(MySQL 8.0+)看嵌套结构,最外层节点才是驱动表 - 在测试环境开
optimizer_trace,查chosen_range_access_summary段,确认优化器为什么选这个表当驱动
驱动表选择背后是成本模型博弈,不是语法位置决定的——最容易被忽略的是,你以为的“小表”,在优化器眼里可能因为缺乏索引或统计偏差,被当成大表处理。











