驱动表where字段必须有索引,被驱动表on字段须建独立单列索引,left join过滤条件应写在on中,必要时用straight_join强制小表驱动,且需确保字段类型与字符集一致。

驱动表的WHERE条件字段必须有索引
MySQL的JOIN性能瓶颈往往不在ON字段,而在于驱动表是否能用索引快速过滤出小结果集。如果驱动表WHERE后仍返回几十万行,后续对被驱动表的每次匹配都变成海量IO,索引再好也救不回来。
常见错误现象:EXPLAIN中驱动表type为ALL或index,rows值远超预期(比如预估128万行),Extra里出现Using where但没走索引。
- 确认驱动表的过滤字段(如
orders.status、users.created_at)是否已建单列索引,或是否落在联合索引最左前缀上 - 避免在WHERE中对字段做函数操作,例如
WHERE DATE(created_at) = '2024-01-01'会让created_at索引失效 - 若过滤条件含多个字段(如
status = 'paid' AND region = 'CN'),优先建联合索引,顺序按选择性从高到低排,比如INDEX(status, region) - 用
SHOW INDEX FROM table_name核对索引是否存在,别只看CREATE TABLE语句——线上表可能被人工删过索引
被驱动表的ON字段必须有独立单列索引
MySQL对被驱动表的JOIN字段查索引,只认「最左匹配」。复合索引的前缀无法替代单列索引用于等值JOIN,这是最容易被忽略的硬规则。
常见错误现象:EXPLAIN中被驱动表key列为NULL,type是ALL,即使你确信该字段“有索引”。
- 为每个被驱动表的ON字段单独建单列索引,例如
ALTER TABLE order_items ADD INDEX idx_order_id (order_id) - 不要指望
INDEX(order_id, product_id, quantity)能用于ON oi.order_id = o.id——order_id不是最左列时无效;若是INDEX(order_id, ...),它可用于JOIN,但不能替代单列索引做高选择性WHERE - 用
EXPLAIN FORMAT=TREE确认实际执行时是否命中你建的索引;如果没命中,大概率是字段类型或字符集不一致 - 检查
SHOW CREATE TABLE输出,比对两边字段的DATA_TYPE(如BIGINTvsINT)、COLLATION(如utf8mb4_unicode_civslatin1_swedish_ci),不一致会导致隐式转换,索引直接失效
LEFT JOIN的过滤条件必须写在ON里,否则索引白建
LEFT JOIN中把右表的过滤条件误放WHERE,不仅改变语义(退化为INNER JOIN),还会让优化器放弃使用右表索引,强制全表扫描后再过滤。
常见错误现象:SQL逻辑本意是“查所有用户及其已发货订单”,但写成LEFT JOIN orders o ON u.id = o.user_id WHERE o.status = 'shipped',结果只返回有发货订单的用户,且orders表全表扫描。
-
ON只放关联逻辑(u.id = o.user_id)和被驱动表的窄化条件(如o.deleted = 0),前提是该字段有索引且能显著减少匹配行数 -
WHERE只放最终结果过滤,尤其是驱动表字段的条件必须在这里写;右表字段的过滤若必须保留LEFT语义,就只能写进ON - 如果
ON里用了非等值条件(如o.amount > 100),MySQL通常无法用索引加速JOIN,应尽量避免 - 测试时用
SELECT COUNT(*)对比两种写法的结果行数差异,能快速暴露语义错误
用STRAIGHT_JOIN强制小表当驱动表
优化器依赖统计信息做成本估算,但统计信息可能过期或不准。当明确知道某张表过滤后只有几百行,而另一张表有百万行时,别赌优化器会选对,直接用STRAIGHT_JOIN锁定顺序。
常见错误现象:SQL字面是FROM large_table JOIN small_table,但EXPLAIN显示large_table被当作驱动表,rows高达百万级。
- 改写为
SELECT STRAIGHT_JOIN ... FROM small_table JOIN large_table ON ...,强制small_table为驱动表 -
STRAIGHT_JOIN仅适用于INNER JOIN;LEFT/RIGHT JOIN不能用,需改用FORCE INDEX配合调整表顺序 - 搭配
USE INDEX提示更稳妥,例如FROM users USE INDEX (idx_status_created) JOIN orders ... - 上线前务必压测:
STRAIGHT_JOIN在数据分布突变时可能反成性能陷阱,比如某天small_table突然膨胀十倍
STRAIGHT_JOIN,如果被驱动表的ON字段没索引,或者字段类型不一致,整个连接还是会退化成Block Nested-Loop,IO爆炸。所以索引配置必须和表结构、查询意图严格对齐,少一个环节,前面所有优化都归零。











