不加索引的JOIN字段等于生产环境裸奔;因MySQL默认Nested-Loop Join需驱动表每行都全表扫描被驱动表,10万×100万=1000亿次比对,ON两侧字段必须严格一致建索引且类型字符集匹配,否则隐式转换使索引失效。

直接说结论:不加索引的 JOIN 字段,基本等于在生产环境裸奔;而嵌套循环本身无法消除,但能通过控制驱动表大小、避免全表扫描来大幅压缩实际循环次数。
为什么JOIN字段必须建索引?
MySQL 的 INNER JOIN 和 LEFT JOIN 默认走 Nested-Loop 算法:从驱动表取一行,去被驱动表逐行比对。如果被驱动表没索引,就得全表扫一遍——10 万行驱动 × 100 万行被驱动 = 1000 亿次比对。
-
EXPLAIN中type列显示ALL或index,基本可断定缺索引 - 索引必须建在
ON子句两侧字段上,比如orders.user_id = users.id,则orders.user_id和users.id都要索引 - 两个字段类型、字符集必须严格一致,否则隐式转换会让索引失效(例如
VARCHAR(32)关联VARCHAR(64))
如何判断哪张表该当驱动表?
驱动表决定外层循环次数,选错会放大性能灾难。不是“左表一定驱动”,而是看数据量和过滤条件:
-
INNER JOIN:优化器通常自动选小结果集表为驱动表,但WHERE条件提前过滤后的真实行数才是关键 -
LEFT JOIN:左边表固定是驱动表,所以务必确保它经过WHERE过滤后足够小(比如加status = 'PAID'后只剩几百行) - 用
STRAIGHT_JOIN强制顺序时,把预估返回行最少的表放最左,再依次接关联表
嵌套循环能避免吗?哪些操作会偷偷扩大循环量?
嵌套循环是 MySQL 关联执行的底层机制,无法跳过,但可以限制它实际跑多远:
- 别在
JOIN条件里用函数,比如ON DATE(order_time) = DATE(create_time),会导致索引失效,退化成全表扫 - 避免
SELECT *,尤其关联大文本字段(TEXT、BLOB),会拖慢join_buffer加载和网络传输 -
ORDER BY和LIMIT放在最终结果上,别放在子查询里——否则 MySQL 可能先生成百万行中间集再排序 - 检查
join_buffer_size是否太小(默认仅 256KB),大关联时可适当调高,但别超过innodb_buffer_pool_size的 10%
真正容易被忽略的是:索引建了≠生效了。务必用 EXPLAIN 看 key 列是否非空、rows 是否合理,而不是只信“我加过索引”这句口头承诺。


















