join字段必须有索引,否则触发全表扫描;索引须建在on子句列上,复合索引注意顺序;小结果集应作驱动表;避免join条件中使用函数。

JOIN 字段必须有索引,否则就是全表扫描
MySQL 在执行 INNER JOIN、LEFT JOIN 时,如果连接条件(如 ON t1.user_id = t2.user_id)中任意一侧字段没索引,优化器大概率放弃使用索引,转为嵌套循环 + 全表扫描。尤其当被驱动表(右表)数据量大时,性能断崖式下降。
- 检查方式:
EXPLAIN SELECT ... JOIN ...
中看type是否为ALL或index,key列是否为NULL - 索引要建在
ON子句实际参与比较的列上,不是WHERE里的列 —— 即使 WHERE 条件更“关键”,没在 ON 里用,对 JOIN 效率无直接帮助 - 复合索引需注意顺序:若写
ON a.x = b.x AND a.y = b.y,则b(x,y)索引有效,b(y,x)通常无效(除非 y 是常量)
驱动表选择影响巨大,小结果集应作驱动表
MySQL 的 JOIN 是单向嵌套循环:先取驱动表(左表)一行,再根据 ON 条件去被驱动表(右表)查匹配行。所以驱动表越小,外层循环次数越少,整体 I/O 和 CPU 开销越低。
- 优化器一般能自动选,但复杂查询(含子查询、函数、OR 条件)可能误判;可用
STRAIGHT_JOIN强制指定驱动表顺序 - 避免在驱动表上用
SELECT *或大字段(如TEXT),减少内存拷贝和网络传输开销 - 如果 LEFT JOIN 的左表加了
WHERE过滤但没走索引,可能导致实际驱动表变大——先确认WHERE是否命中索引,再谈 JOIN 顺序
避免在 JOIN 条件中使用函数或表达式
一旦在 ON 或 USING 中对字段做运算,比如 ON DATE(t1.created_at) = DATE(t2.date),该字段就无法使用索引 —— MySQL 无法将函数结果与索引 B+ 树结构对齐。
- 正确做法是预计算或改写逻辑:例如把日期范围下推到 WHERE,用
t1.created_at >= '2024-01-01' AND t1.created_at 替代 <code>DATE() - 同理,
ON UPPER(t1.name) = UPPER(t2.name)会失效;若业务允许,统一存小写并建函数索引(MySQL 8.0+ 支持CREATE INDEX idx_name ON t1 ((LOWER(name)))) - 隐式类型转换也危险:
ON t1.id = t2.user_id中若t1.id是INT、t2.user_id是VARCHAR,MySQL 会把后者全转成数字比对,索引失效
覆盖索引 + 延迟关联可大幅减少回表
当 JOIN 后还要查大量非索引字段(如 SELECT t1.*, t2.content),MySQL 不得不反复回主键索引捞数据,I/O 成倍增加。覆盖索引本身不能跨表,但可通过“延迟关联”模式规避。
- 典型写法:
SELECT t1.*, t2.content FROM t1 INNER JOIN (SELECT id FROM t2 WHERE status=1) AS t2_ids ON t1.t2_id = t2_ids.id INNER JOIN t2 ON t2.id = t2_ids.id
—— 先用最小结果集驱动,再补全字段 - 确保子查询
(SELECT id FROM t2 WHERE ...)能走索引(哪怕只是status单列索引),且返回行数可控 - 如果业务允许,尽量让
SELECT只取必要字段,配合联合索引把常用查询字段都包含进去(如INDEX idx_cover (a,b,c)支持SELECT a,b WHERE a=? AND b=?)
EXPLAIN 几乎发现不了。










