LEFT JOIN性能差于INNER JOIN的根本原因在于执行时裁剪自由度低:必须保留左表所有行、强制左表为驱动表、索引易失效、WHERE误用导致逻辑退化。

INNER JOIN能提前丢弃不匹配行,LEFT JOIN必须保留所有左表行
根本区别不在语法轻重,而在数据库执行时的“裁剪自由度”。INNER JOIN只要发现某行在右表无匹配,整行立刻丢弃,不参与后续任何计算;而LEFT JOIN哪怕右表99%的行都找不到对应,也得为左表每一行分配内存、填充NULL、构造结果字段。这导致两个隐性开销:一是结果集体积膨胀(尤其右表列多时),二是无法跳过大量无效扫描。
LEFT JOIN强制左表作驱动表,容易放大嵌套循环成本
MySQL默认用Nested-Loop Join算法,其性能高度依赖驱动表大小。对INNER JOIN,优化器可自由选择小表作驱动表;但LEFT JOIN语义上锁定左表为“主表”,必须以它为外层循环。如果左表是千万级用户表,右表只是百条配置项,那就要循环千万次去查右表——哪怕加了索引,I/O和CPU开销也远超必要。
- 验证方法:用
EXPLAIN看type列,若左表出现ALL或range而右表是ref,基本就是驱动表选错 - 补救思路:把大表挪到
RIGHT JOIN的右边,或改写为子查询先过滤左表
ON条件里写函数会让LEFT JOIN性能雪崩
LEFT JOIN对索引失效更敏感。比如写ON a.id = UPPER(b.ref_id),右表b就完全无法走ref_id索引——因为函数使索引列值不可预判,数据库只能对每条左表记录逐行计算UPPER()再比对。而INNER JOIN因匹配行少,这种恶化常被掩盖。
- 正确做法:统一存储格式(如全存小写),或MySQL 8.0+建函数索引:
CREATE INDEX idx_ref_upper ON b ((UPPER(ref_id))) - 警惕隐式转换:
ON a.id = b.ref_id中若a.id是INT、b.ref_id是VARCHAR,也会触发全表扫描
WHERE里错放右表条件,LEFT JOIN就白写了
这是最隐蔽的性能陷阱:LEFT JOIN b ON a.id = b.a_id WHERE b.status = 'paid'表面想筛已支付订单,实际把所有b.status IS NULL的行(即左表无订单用户)全过滤掉了——效果等同INNER JOIN,还多花了LEFT JOIN初始化的开销。
- 口诀:“连什么”写
ON,“要不要这行”才写WHERE - 要保留所有用户但只显示已支付金额 → 把
b.status = 'paid'移到ON子句 - 真要全局排除测试数据 → 只能作用于左表字段,如
WHERE a.is_test = 0
真正卡住LEFT JOIN的,往往不是“多返回几行”,而是它被迫承担的NULL填充、驱动表锁定、索引失效容忍度低、以及WHERE误用带来的逻辑退化。这些点一旦叠加,执行时间就从毫秒跳到秒级——而问题常藏在执行计划里,不看EXPLAIN根本意识不到。










