LEFT JOIN慢主因是驱动表ON字段无索引或右表关联字段类型不一致致索引失效;需在左表ON字段建单列索引,右表ON字段也须有类型严格一致的索引,并将WHERE/ORDER BY字段纳入联合索引。

LEFT JOIN 慢,八成不是语法问题,而是驱动表的 ON 字段没索引,或者右表的关联字段类型不一致、隐式转换导致索引失效。
LEFT JOIN 的驱动表 ON 字段必须有索引
MySQL 默认以左表为驱动表,它会逐行扫描左表,再拿每行的关联值去右表找匹配。如果左表 ON 字段没索引(比如 orders.user_id),就会全表扫描左表——哪怕右表有主键也没用。
- 查执行计划:看到
EXPLAIN中左表的type = ALL或key = NULL,就是铁证 - 建索引要直接落在驱动表的
ON字段上:CREATE INDEX idx_orders_user_id ON orders(user_id) - 别指望复合索引里带这个字段就能“顺便走”——例如
INDEX(created_at, user_id)对ON o.user_id = u.id无效,最左前缀不匹配
被驱动表(右表)的 ON 字段也得有索引,且类型严格一致
右表没索引,等于左表每扫一行,右表就得全表扫一遍,复杂度接近笛卡尔积。更隐蔽的是:即使字段有索引,只要类型不一致,MySQL 就会做隐式转换,索引直接失效。
- 用
SHOW CREATE TABLE核对两边字段:是否同为INT或同为BIGINT UNSIGNED;字符集和COLLATE是否一致 - 常见翻车点:
users.id是INT,orders.user_id是VARCHAR(20)→ 全表扫描右表 - 右表索引必须是独立的单列或复合索引的最左前缀,不能依赖主键索引“顺带覆盖”
WHERE 和 ORDER BY 字段要纳入联合索引,否则触发 filesort
光让 ON 字段走索引不够。如果查询带 WHERE u.status = 'active' 或 ORDER BY o.amount DESC,而这些字段不在索引里,MySQL 就得把 JOIN 后的结果临时排序,出现 Using temporary; Using filesort。
- 联合索引顺序按“等值 → 范围 → 排序”排:
INDEX(status, created_at, amount)支持WHERE status = ? AND created_at > ? ORDER BY amount DESC -
IN算范围条件,它后面的字段无法用于排序——别把ORDER BY字段塞在IN字段后面 - 字段数别超 3–4 个;宽字段(如
VARCHAR(500))加进索引,体积暴涨,维护成本高
LEFT JOIN 的 WHERE 条件误写会让语义退化,索引策略也跟着错
SELECT * FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE u.deleted = 0 表面是 LEFT,实际效果等价于 INNER JOIN——因为 WHERE 过滤了右表非空行,但执行路径仍是 LEFT 的全量扫描逻辑,性能更差。
- 想保留 LEFT 语义又过滤右表?把条件写进
ON:LEFT JOIN users u ON o.user_id = u.id AND u.deleted = 0 - 如果业务真不需要左表全量(比如只查“有用户信息的订单”),直接改用
INNER JOIN,优化器能更自由选驱动表 - 定期运行
ANALYZE TABLE更新统计信息,否则优化器可能因数据倾斜或过期统计误判索引有效性
最常被忽略的一点:LEFT JOIN 的索引有效性极度依赖统计信息准确性和字段类型一致性,光建了索引不等于就生效了——务必用 EXPLAIN FORMAT=TREE 看真实驱动顺序和索引使用情况,而不是只看有没有 key 字段。










