inner join通常比left join快,因其仅处理匹配行,而left join需扫描左表全部数据并为无匹配行补null,导致i/o和cpu开销更高;且left join后where条件可能意外转为inner join语义,应将右表过滤条件移至on子句。

为什么INNER JOIN通常比LEFT JOIN快
因为INNER JOIN只处理匹配行,而LEFT JOIN必须扫描左表全部数据,再逐行尝试匹配右表——哪怕右表没对应记录,也要补NULL。这直接导致I/O和CPU开销上升。
实操建议:
- 业务上能确认“一定有关联数据”的场景(比如查订单+用户信息),优先用
INNER JOIN,别图省事写成LEFT JOIN -
LEFT JOIN后跟WHERE条件时特别危险:例如LEFT JOIN b ON a.id = b.a_id WHERE b.status = 'paid',会把本该保留的左表空匹配行也过滤掉,实际等效于INNER JOIN,但执行计划可能更差 - 真需要左表全量+右表筛选结果,把右表条件挪到
ON里:LEFT JOIN b ON a.id = b.a_id AND b.status = 'paid'
驱动表选错会导致全表扫描
MySQL优化器决定哪张表先查(驱动表),哪张表后查(被驱动表)。对INNER JOIN,它通常选小表当驱动表;但一旦连接条件中只有一边有索引,有索引的那张表大概率被当作被驱动表——如果这张表很大,就会扫全表。
实操建议:
- 用
EXPLAIN看type列:如果是ALL或index,说明走了全表或全索引扫描,得查是不是驱动表错了 - 强制指定驱动表:用
STRAIGHT_JOIN(仅限INNER JOIN),例如SELECT STRAIGHT_JOIN ... FROM small_table JOIN big_table ON ... - 确保连接字段两边都有索引,且类型一致(比如
INT对INT,不是VARCHAR对INT)
联合索引要覆盖JOIN + WHERE + SELECT字段
只给ON字段建单列索引远远不够。MySQL在JOIN过程中可能需要回表取值,或者因WHERE或ORDER BY触发临时表——这些都源于索引没覆盖完整路径。
实操建议:
- 联合索引顺序按“连接字段 → 过滤字段 → 排序字段 → 查询字段”排列,例如查询
SELECT name, price FROM orders o JOIN users u ON o.user_id = u.id WHERE u.city = 'Shanghai' ORDER BY o.created_at,则users表建索引(city, id),orders表建(user_id, created_at, name, price) - 避免在索引字段上用函数:
ON YEAR(o.created_at) = 2024会让索引失效,改成o.created_at BETWEEN '2024-01-01' AND '2024-12-31' -
SELECT *是性能杀手,尤其多表JOIN时——数据库要拼接所有字段,网络传输、内存占用、排序开销都翻倍
大表关联前务必先过滤
JOIN执行顺序是:先做FROM和ON,再做WHERE。如果WHERE里有大表的过滤条件,但写在最后,MySQL可能已经把几百万行拉出来再筛,而不是先缩小数据集再JOIN。
实操建议:
- 从表(被JOIN的表)的过滤条件,写进子查询或
ON,别堆在末尾WHERE里。例如:FROM a JOIN (SELECT * FROM b WHERE dt = '2026-08-01') b ON a.id = b.a_id - 主表(驱动表)的过滤条件可以放心放
WHERE,但如果有多个条件,优先把高选择性(能筛掉90%以上数据)的条件放前面 - 时间分区表务必用
dt这类分区字段做前置过滤,否则可能扫全分区
真实线上问题往往卡在“以为索引建了就万事大吉”,其实JOIN路径上的每一步——驱动表选择、索引覆盖粒度、过滤时机——都可能成为瓶颈。调试时别只盯着EXPLAIN的rows,更要盯Extra里有没有Using temporary或Using filesort。











