JOIN字段无索引会导致查询慢十倍;需为ON子句字段建B-tree单列索引,外键列不自动索引;驱动表选错引发Using join buffer;复合条件应建联合索引(t1_id, status)并注意类型一致与隐式转换。

JOIN字段没索引,查询直接变慢十倍
MySQL执行JOIN时,如果关联字段(比如ON t1.id = t2.t1_id中的t1.id或t2.t1_id)没有索引,优化器大概率会走全表扫描。尤其当被驱动表(通常是JOIN右边的表)数据量稍大,性能断崖式下跌。不是“可能慢”,是几乎必然慢。
实操建议:
- 对
ON子句中出现的每个字段,单独检查是否已有索引:SHOW INDEX FROM table_name;
- 若字段常用于
JOIN且区分度高(如外键),优先建B-tree单列索引;不要依赖联合索引的“前缀匹配”来支撑JOIN,除非该字段恰好是联合索引最左列 - 注意:主键自动有索引,但外键列不会自动创建索引——哪怕你加了
FOREIGN KEY约束,也必须显式建索引
驱动表选错导致Extra: Using join buffer
MySQL默认用BNL(Block Nested-Loop)算法处理JOIN,当驱动表小、被驱动表大且后者无可用索引时,就会启用join_buffer。这时EXPLAIN里会出现Extra: Using join buffer,意味着大量数据被载入内存做嵌套循环,I/O和CPU压力陡增。
关键判断点:
- 看
EXPLAIN输出的table列顺序:排在前面的是驱动表,后面的是被驱动表 - 确保驱动表是结果集最小的那个(不一定是物理行数最少,而是经过
WHERE过滤后实际参与JOIN的行数最少) - 用
STRAIGHT_JOIN强制指定驱动表顺序(仅当确认优化器选错时):SELECT STRAIGHT_JOIN ... FROM small_table t1 JOIN large_table t2 ON t1.id = t2.t1_id;
-
join_buffer_size调大能缓解但治标不治本;真正要解决的是让被驱动表能走索引查找(即上一条说的字段加索引)
复合条件JOIN时,联合索引怎么设计
当JOIN同时带WHERE过滤(如ON t1.id = t2.t1_id WHERE t2.status = 'active'),只给t2.t1_id建单列索引往往不够。优化器可能放弃使用该索引,转而全表扫描再过滤。
此时应建覆盖JOIN + WHERE条件的联合索引:
- 把
JOIN字段放最左(因ON匹配需最左前缀) - 紧接
WHERE中的等值条件字段(如status) - 最后可加
SELECT中需要的其他字段,实现“索引覆盖”,避免回表 - 示例:对
t2建索引(t1_id, status, name),适用于JOIN ... ON t1.id = t2.t1_id WHERE t2.status = 'active' - 注意:
ORDER BY或GROUP BY字段若也出现在查询中,可考虑加入联合索引末尾,但需权衡写入开销
EXPLAIN中type=ALL或type=index是危险信号
EXPLAIN输出里的type字段直接反映访问类型。ALL代表全表扫描,index代表全索引扫描(仍需遍历整个索引树),这两种都说明没命中有效索引用于定位JOIN行。
排查步骤:
- 先确认
key列是否为NULL——是则肯定没走索引 - 若
key非空但type仍是ALL或index,常见原因是:字段类型不一致(如INTvsVARCHAR)、隐式类型转换、函数包裹字段(如ON t1.id = CAST(t2.t1_id AS SIGNED)) - 检查字符集和排序规则是否完全一致:
SHOW CREATE TABLE对比两边字段的COLLATE - 避免在
ON字段上使用函数、表达式或LIKE '%xxx'这类无法利用索引的操作










