JOIN 字段没索引,查询直接变慢十倍
MySQL 的
JOIN操作本身不慢,慢的是驱动表(左表)每扫一行,被驱动表(右表)都得全表扫描匹配一次。如果
ON条件字段没索引,就会触发这种嵌套循环的暴力匹配。
实操建议:
检查EXPLAIN输出中
type列是否为
ALL或
index—— 这说明走了全表或全索引扫描,不是高效查找 确保
ON子句两边的字段类型完全一致(比如都是
INT,不能一边是
INT一边是
VARCHAR),否则即使建了索引也用不上 复合索引要遵循最左前缀原则:若
ON a = b AND c = d,优先在被驱动表上建
(b, d)索引,而不是只建
b
小表驱动大表,但 MySQL 不一定听你的话
理论上应该让结果集更小的表做驱动表,减少外层循环次数。但 MySQL 的查询优化器会自己决定驱动顺序,
STRAIGHT_JOIN是唯一能强制指定的方式。
实操建议:
用EXPLAIN FORMAT=TREE查看实际驱动顺序,别只看 SQL 里写的先后顺序 当优化器选错时(比如误判小表为大表),在
SELECT后加
STRAIGHT_JOIN,并把预期的小表放前面 注意:
STRAIGHT_JOIN会禁用优化器重排,一旦数据分布变化,可能反而更差,适合稳定、已知规模的场景
临时表和排序导致 JOIN 性能雪崩
当
JOIN后需要
GROUP BY、
ORDER BY或
SELECT *且字段太多时,MySQL 可能创建隐式临时表,甚至落盘(
Using temporary; Using filesort),IO 成为瓶颈。
实操建议:
只查真正需要的字段,避免SELECT *,尤其不要跨表查大文本字段(如
TEXT) 如果必须排序,确保
ORDER BY字段在驱动表或被驱动表的索引中,并尽可能覆盖在同一个复合索引里(例如
(join_key, sort_col)) 观察
EXPLAIN中的
Extra列,出现
Using temporary就要警惕,优先考虑改写逻辑或加覆盖索引
关联字段存在 NULL,索引可能失效
当
ON t1.a = t2.b中任意一边的字段允许
NULL,且实际数据中有大量
NULL值,MySQL 可能放弃使用该字段上的索引——因为 B+ 树索引默认不存储
NULL,优化器估算选择性变差。
实操建议:
建表时尽量避免JOIN字段设为
NULLABLE;如业务允许,用
0或特殊值替代
NULL若无法修改表结构,可在
ON条件中显式排除
NULL,例如
t1.a = t2.b AND t1.a IS NOT NULL AND t2.b IS NOT NULL,帮助优化器判断可选索引范围 用
SHOW INDEX FROM table_name确认索引是否包含该字段,再结合
SELECT COUNT(*) FROM t WHERE col IS NULL评估 NULL 比例 索引不是建了就生效,JOIN 的执行路径高度依赖字段类型一致性、NULL 处理、以及优化器对数据分布的预估——这些地方一不留神,索引就成摆设。
