为什么 JOIN
查询变慢了?先看执行计划
MySQL 的
JOIN慢,90% 不是语法问题,而是优化器选错了驱动表或没走索引。必须用
EXPLAIN看实际执行路径,重点关注
type(是否为
ref/
range,避免
ALL)、
rows(预估扫描行数)、
Extra(是否出现
Using join buffer或
Using temporary)。
常见误判:看到
JOIN就加索引——但若连接字段类型不一致(比如
INT对
VARCHAR),索引会失效;或者被驱动表有复合索引但顺序错(如
ON a.x = b.x AND a.y = b.y,但索引是
(y, x)),照样全表扫。
STRAIGHT_JOIN
强制驱动表时要注意什么
当优化器选错驱动表(比如小表没被选为驱动表),可用
STRAIGHT_JOIN手动指定。但它不是银弹: 只对当前语句生效,无法被查询缓存复用(如果还开着的话) 如果数据分布变化(比如某表突然膨胀),原来最优的驱动顺序可能变差 在从库上执行时,若主从表统计信息不同步,
STRAIGHT_JOIN可能让从库更慢
建议只在明确知道驱动表应为何、且该查询高频稳定时使用;上线前务必在从库也验证
EXPLAIN输出。
多表 JOIN
时,WHERE
条件放哪儿影响很大
条件写在
ON还是
WHERE,对结果和性能都可能不同,尤其涉及
LEFT JOIN:
ON中的条件用于决定如何匹配,不满足也会保留左表记录(右表字段为
NULL)
WHERE中的条件是在
JOIN完成后过滤,会把
NULL行直接剔除,等价于转成
INNER JOIN
性能上,把能提前过滤的条件尽量塞进
ON(特别是被驱动表的字段),让关联前就缩小结果集。例如:
LEFT JOIN order_items oi ON o.id = oi.order_id AND oi.status = 'shipped'比
LEFT JOIN ... ON o.id = oi.order_id WHERE oi.status = 'shipped'更高效,后者会先生成大量
NULL行再过滤。
临时表和排序开销大?检查 join_buffer_size
和字段长度
当
EXPLAIN显示
Using join buffer (Block Nested Loop),说明 MySQL 在内存里缓存驱动表部分数据做批量匹配。但这个 buffer 大小受
join_buffer_size控制,默认才 256KB,很容易溢出,导致磁盘临时表甚至排序。
真正有效的调优点是:
确保连接字段类型完全一致(包括字符集、是否NOT NULL),否则无法使用
join buffer减少
SELECT *,只查真正需要的字段;过长的
VARCHAR或
TEXT字段会让每行占用 buffer 更多空间 如果必须关联大字段,考虑把关联逻辑提到应用层,用两次查询+内存哈希代替单次
JOIN
别盲目调大
join_buffer_size——它按线程分配,设太高会快速耗尽内存,反而触发 swap。
