驱动表应选type为all或index且rows值小的表,mysql优化器依据where过滤后实际参与join的行数(explain的rows列)而非表物理大小决定驱动表。

看EXPLAIN的type和rows,不是看表物理大小
驱动表是否合理,不能靠“这张表有10万行,那张有1000万行”来猜。MySQL优化器真正参考的是WHERE过滤后、实际参与JOIN的行数,这个数值体现在EXPLAIN输出的rows列里。你得盯住它,而不是SHOW TABLE STATUS里的Data_length。
关键判断信号:
- 驱动表的
type应为ALL或index(全表或索引扫描),rows值要小(比如 - 被驱动表的
type应为ref、eq_ref或range,key列显示有效索引名,rows接近实际匹配数 - 如果某张表
type=ALL且rows高达几十万,但它在EXPLAIN输出中排第二行或之后,基本就是被错当成了被驱动表,却承担了驱动表的扫描量
LEFT JOIN左表被强制驱动?别信,先看EXPLAIN
LEFT JOIN语法上保证左表记录不丢,但**不保证左表一定被选为驱动表**。优化器可能发现右表加WHERE后只剩几百行,而左表即使有强条件也剩5万行,它就会把右表“提上来”当驱动表——只要最终结果逻辑等价,它就敢这么干。
常见误判场景:
- 写
SELECT * FROM big_table LEFT JOIN small_table ON ... WHERE small_table.status = 'active',实际上已退化为INNER JOIN语义,优化器很可能让small_table当驱动表 -
ON里带函数(如ON DATE(t1.create_time) = t2.date)或类型隐式转换(如INT对VARCHAR),导致右表无法走索引,优化器被迫回退到扫左表 - 左表没加任何WHERE条件,
rows直接等于总行数,哪怕它是“小配置表”,一旦数据膨胀也会暴雷
加STRAIGHT_JOIN前后对比key和Extra
加STRAIGHT_JOIN不是为了“看起来更规范”,而是为了验证你的直觉是否正确。它只在优化器明显误判时才该用,生效与否必须用EXPLAIN实锤。
真正起效的标志:
-
key列从NULL变成具体索引名(比如idx_user_id),说明被驱动表终于用上索引了 -
Extra里Using join buffer (Block Nested Loop)消失,或从被驱动表那一行列移到驱动表上(说明缓存压力转移,是好事) -
rows列数值显著下降,尤其是被驱动表的rows从百万级降到千级以内 - 注意:
STRAIGHT_JOIN只对FROM子句中直接出现的表生效,子查询、视图、CTE包裹后就失效
字段类型、索引、统计信息三者缺一不可
就算你把小结果集放左边,只要被驱动表的ON字段没索引、类型不一致、或统计信息过期,驱动表选得再准也没用——内层循环照样全表扫。
必须同步检查:
- 用
SHOW CREATE TABLE核对JOIN字段:类型(INTvsBIGINT)、长度(VARCHAR(50)vsVARCHAR(100))、是否允许NULL、字符集与COLLATION,全部严格一致 - 索引建在被驱动表上,不是驱动表;复合ON条件(如
ON a.x = b.x AND a.y = b.y)要建联合索引(x, y),顺序必须匹配 - 执行
ANALYZE TABLE更新统计信息,否则优化器基于过期rows估算成本,选错概率陡增
最常被忽略的点:LEFT JOIN右表、RIGHT JOIN左表,最容易漏掉索引。它们不是“被动接收方”,而是高频被探查的目标,必须单独检查并补索引。











