MySQL-JOIN优化-NLJ与BNL的区别与实战

MySQL JOIN 优化:NLJ 与 BNL 的区别与实战

两个表 join 查询突然变慢,是很多后端同学都遇到过的问题。本文通过一个真实的慢查询案例,讲清楚 MySQL 两种 join 算法(NLJ 与 BNL)的原理,以及如何把一次查询从 8 秒优化到 30 毫秒。

一、问题案例

一个订单列表接口,从 100ms 飙到 8 秒,SQL 就一句 join:

sql 复制代码
SELECT o.*, u.nickname
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at >= '2026-08-01';

两个表的 join 字段都有索引,EXPLAIN 的 type 也不是 ALL,但 Extra 里有一行关键信息------Using join buffer (Block Nested Loop)。

二、原理:NLJ 与 BNL

MySQL 执行 join 有两种算法:

NLJ(Index Nested-Loop Join)

驱动表逐行取出,被驱动表走索引 匹配。复杂度约为 驱动表行数 × log(被驱动表行数)。

BNL(Block Nested-Loop Join)

当被驱动表没有可用索引 时,MySQL 把驱动表的行读入 join buffer,再对被驱动表做全表扫描 逐行比对。复杂度约为 驱动表行数 × 被驱动表行数。

回到案例:orders 几百万行,users 几十万行。优化器错误地选了 users 当驱动表,去 join 没有索引的 orders,触发了 BNL,几十万 × 几百万,8 秒由此而来。

三、优化方法

两条原则:

  1. 小表驱动大表:把行数少的表放在前面作为驱动表。
  2. 被驱动表的 join 字段必须有索引。
sql 复制代码
SELECT o.*, u.nickname
FROM users u                          -- 小表驱动
JOIN orders o ON o.user_id = u.id     -- orders.user_id 建索引
WHERE o.created_at >= '2026-08-01';

如果优化器仍选错顺序,可用 STRAIGHT_JOIN 强制:

sql 复制代码
SELECT o.*, u.nickname
FROM users u
STRAIGHT_JOIN orders o ON o.user_id = u.id
WHERE o.created_at >= '2026-08-01';

优化后,查询从 8 秒降到 30 毫秒。

四、为什么优化器会选错

优化器依赖统计信息估算成本,当表的统计信息(行数、区分度)过期时,估算就会失真。这也是为什么 EXPLAIN 里的 rows 只能作参考------必要时记得 ANALYZE TABLE 更新统计信息。

五、总结

  • join 慢,先看 EXPLAIN 的 Extra,出现 Block Nested Loop 就是被驱动表没走索引。
  • 优化方向:小表驱动大表 + 被驱动表 join 字段建索引。
  • 优化器选错顺序时,用 STRAIGHT_JOIN 强制。

我是无羡(小剑),全栈偏后端的独立开发者。

作品集:无羡 · 独立开发者作品集

如果对你有帮助,欢迎点赞、收藏、关注。

相关推荐
幽灵诶1 小时前
Mysql(基于测试的学习)
数据库·mysql
砚底藏山河1 小时前
python量化自定义周期 K 线合成
java·开发语言·数据库·python·金融
宇宙无敌暴龙战士6662 小时前
MySQL 8 库表操作学习笔记:SQL 基础、库表管理、约束与用户授权全流程
数据库·sql·mysql·学习笔记·后端开发
猿究院_夏燊2 小时前
MySQL 的四种隔离级别
java·数据库·mysql
say_fall2 小时前
【Linux系统编程】Ext 文件系统:磁盘与存储、文件与目录、现代计算机究竟如何寻找文件
linux·运维·服务器·数据库·c++
共享家95273 小时前
408 操作系统第一章
服务器·数据库·windows
这个DBA有点耶4 小时前
连拿六年第一后宣布出海:国产数据库凭什么敢撕掉“平替”的标签?
数据库·人工智能·架构
云和恩墨4 小时前
数据库监控简史:从会回答到能交付——AI-DBA如何成为真正的数字同事
数据库·人工智能·dba
paopaokaka_luck4 小时前
灾害应急物资管理系统(AI应急助手、Echarts可视化数据看板、捐赠入库联动、 物资全流程管理、库存变动追溯、灾情资讯与互动反馈、物资发放公示)
java·前端·javascript·spring boot·mysql·tomcat·echarts
这个DBA有点耶5 小时前
为什么删掉索引后,查询瞬间变快了?探讨MySQL的随机I/O与顺序I/O博弈
数据库·mysql·代码规范