LEFT JOIN 本身不慢,慢是因为左表全量扫描、右表缺索引及优化器选错驱动表;STRAIGHT_JOIN 仅在驱动表明确且右表有索引时有效,否则可能适得其反。

LEFT JOIN 本身不慢,慢是因为它被迫扫描左表全量 + 右表没索引 + 优化器选错驱动表。 直接加 STRAIGHT_JOIN 能绕过优化器误判,但不是万能解药——它只在驱动表明确、右表索引到位时才真正起效。
为什么LEFT JOIN常被误认为“天生慢”
LEFT JOIN 必须返回左表每一行,哪怕右表没匹配项也要补 NULL。这导致两个硬约束:
- 优化器无法像
INNER JOIN那样基于右表条件提前剪枝(比如 WHERE o.status = 'paid' 实际会让 LEFT JOIN 语义退化,但执行路径未必优化) - 若右表
ON字段无索引,MySQL 只能对右表做全表扫描,每扫左表一行就扫一遍右表——即嵌套循环(NLJ),数据量一上来就卡死 - 更隐蔽的是:当左表小、右表大,但优化器因统计信息不准或缺失索引而把右表当驱动表,就会反复扫描小表千万次
STRAIGHT_JOIN 不是“加速开关”,而是“手动指定驱动表”的指令
STRAIGHT_JOIN 强制 MySQL 按 SQL 中表出现的顺序连接,跳过优化器重排。但它只解决“顺序错”的问题,不解决“没索引”或“逻辑冗余”的问题。
- 适用前提:你清楚哪张表过滤后行数最少(比如
users加了WHERE status = 'active'后只剩 200 行,而orders有 500 万行),且该表应为驱动表 - 错误用法:写成
SELECT * FROM orders STRAIGHT_JOIN users ON ...—— 把大表放前面,等于主动让 MySQL 扫描 500 万次users - 正确姿势:
SELECT * FROM users STRAIGHT_JOIN orders ON users.id = orders.user_id WHERE users.status = 'active' - 风险提示:一旦表数据分布变化(比如
users活跃用户暴涨到百万级),STRAIGHT_JOIN反而会拖垮查询,必须配合定期EXPLAIN FORMAT=JSON复核
比STRAIGHT_JOIN更优先检查的三件事
加 STRAIGHT_JOIN 前,请先确认以下三点是否已满足。否则加了也白加:
-
EXPLAIN输出中右表的key列为空?——说明ON字段没索引,先建索引:CREATE INDEX idx_orders_user_id ON orders(user_id) - 右表
ON字段类型/字符集/排序规则是否和左表完全一致?——例如t1.user_id INT对t2.uid VARCHAR,索引必然失效 - 是否在 LEFT JOIN 后又写了
WHERE t2.col IS NOT NULL?——这会让语义等价于 INNER JOIN,但执行计划仍走 LEFT 路径,应直接改写为INNER JOIN
COUNT(*) 配合 LEFT JOIN 变慢的典型陷阱
写 SELECT COUNT(*) FROM t1 LEFT JOIN t2 ON ... 时,MySQL 必须构造完整结果集再计数,哪怕你只关心数量。常见坑点:
- 没加
WHERE过滤左表?——导致全表扫描左表,再对每行去右表查,放大效应极强 - 右表实际无匹配,但因没索引,MySQL 仍要扫全表判断“不存在”,
rows值会暴增 - 想统计“左表中哪些有右表记录”?别用
LEFT JOIN ... WHERE t2.id IS NOT NULL,改用EXISTS:SELECT COUNT(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.t1_id = t1.id)
最易被忽略的一点:STRAIGHT_JOIN 是“手术刀”,不是“止痛片”。它能切掉优化器乱选驱动表的病灶,但治不了右表没索引、字段类型不匹配、WHERE 写错位置这些基础病。上线前务必用 EXPLAIN 看 type 是否为 ref 或 eq_ref,rows 是否接近真实匹配数——否则一切优化都是幻觉。


















