先看EXPLAIN的type是否为ALL或index,再检查key是否为NULL;需确认JOIN字段类型一致、无表达式操作、索引在最左前缀且join_buffer_size足够。

EXPLAIN 看不出索引失效?先确认 type 是不是 ALL 或 index
很多人跑 EXPLAIN 后只扫一眼 key 列是不是 NULL,就下结论“没走索引”。其实更关键的是看 type:如果关联字段没索引,JOIN 时大概率退化成嵌套循环(type = ALL),甚至全表扫描驱动表 + 全表扫描被驱动表(type = index 配合 rows 极大)。这时 Extra 里常带 Using join buffer (Block Nested Loop)——这是 MySQL 在硬扛没索引的 JOIN。
-
EXPLAIN SELECT * FROM orders o JOIN users u ON o.user_id = u.id;中,若orders.user_id无索引,o表可能type=ALL,u表哪怕有PRIMARY KEY也救不回整体性能 - 注意驱动表顺序:MySQL 5.7+ 会自动选小结果集当驱动表,但若两表都大,且关联字段都没索引,
type就双双沦陷 -
SHOW CREATE TABLE比DESCRIBE更可靠——它能暴露字段是否在复合索引的最左前缀里
ALTER TABLE 加索引时,user_id 字段类型必须和 JOIN 条件完全一致
加了索引还是慢?八成是字段类型隐式转换。比如 orders.user_id 是 INT,但 users.id 是 BIGINT,或者一边是 VARCHAR(32)、另一边是 VARCHAR(64),MySQL 会放弃索引走全表扫描。
- 用
SHOW FULL COLUMNS FROM table_name对比两边字段的Type和Collation(字符集/排序规则不一致也会触发转换) -
ALTER TABLE orders ADD INDEX idx_user_id (user_id);前,务必确认user_id和被 JOIN 的字段类型、符号性(SIGNED/UNSIGNED)、长度完全一致 - 字符串字段 JOIN 时,
CHAR和VARCHAR混用、或带空格填充(CHAR默认右填空)也可能导致索引失效
LEFT JOIN 的右表字段加索引无效?重点看 ON 条件里实际参与匹配的列
LEFT JOIN 的右表(users)即使加了索引,如果 ON 条件写成 o.user_id = u.id + 0 或 CAST(u.id AS CHAR),索引照样废掉。MySQL 不会对表达式列自动使用索引。
- 检查
ON子句是否对索引字段做了函数操作、数学运算、类型转换 -
LEFT JOIN users u ON o.user_id = u.id AND u.status = 'active'—— 这里u.id索引有效,但u.status如果没包含在复合索引里,WHERE下推可能受限 - 想让右表过滤更早生效,可把条件从
ON挪到WHERE(但注意语义变化:挪到WHERE会让LEFT JOIN变成INNER JOIN效果)
JOIN 字段索引生效但查询仍慢?检查 join_buffer_size 和磁盘临时表
索引有了,EXPLAIN 显示 type=ref,但查询还是卡——可能是连接结果集太大,MySQL 把中间结果写到了磁盘临时表,或者 join_buffer 不够用,频繁换页。
- 查
SHOW STATUS LIKE 'Created_tmp%';,如果Created_tmp_disk_tables持续增长,说明 JOIN 结果溢出内存,正在用磁盘临时表 -
join_buffer_size默认才 256KB,大表 JOIN 前建议调到 4–8MB(注意是每个线程独占,别设太高撑爆内存) - 用
SELECT ... STRAIGHT_JOIN ...强制驱动表顺序,避免优化器误判;配合FORCE INDEX指定关联索引,减少计划抖动
索引不是万能解药,JOIN 关联字段类型对齐、表达式洁癖、内存缓冲配置,三个地方漏一个,慢查询照旧。真正卡住的往往不是“有没有索引”,而是“索引能不能被干净地用上”。










