news 2026/4/18 10:29:00

MySQL 多表关联,最高效的查询方式:NLJ ,这样用性能翻5倍

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 多表关联,最高效的查询方式:NLJ ,这样用性能翻5倍

概念

NLJ,全称Index Nested-Loop Join(基于索引的嵌套循环关联)。它是MySQL 在执行 JOIN 时最常用、也是效率最高的算法之一。

它的核心思想是:用驱动表的每一行,去被驱动表的索引里做等值查找,从而把“内层全表扫描”变成“索引点查”,复杂度从 O(N×M) 降到 O(N×logM)。

首先说出结论:NLJ是非常棒的方案,效能非常高,99%的情况建议大家使用NLJ。

示例

表 student1 和 student2 结构如下:

CREATE TABLE `student1` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(200), `no` varchar(100), PRIMARY KEY (`id`), ) ENGINE=InnoDB CREATE TABLE `student2` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255), `no` varchar(255), PRIMARY KEY (`id`), KEY `idx_no` (`no`) ) ENGINE=InnoDB

说明:表student1有100条数据,表student2有999条数据;表 student2 的 no 字段建了索引。

执行如下SQL:

explain select s1.id, s1.name, s1.no, s2.name from student1 s1 join student2 s2 on s2.no = s1.no

分析explain结果得出:

A、驱动表是s1,被驱动表是s2(查看方法见 通过Explain分析驱动表)。

B、对表s1进行了全表扫描(通过type=ALL得出);关联表 s2 用上了表s2的字段no上的索引(通过 key=idx_no 得出)。

这个语句的执行流程是这样的:

1、从表 s1 中读入一行数据 M;

2、从数据行M中,取出no字段到表s2的索引no上去查找;

3、取出表s2中满足条件的行,跟M组成一行,作为结果集的一部分;

4、重复执行步骤1到3,直到表s1的末尾循环结束。

这个过程是先遍历表s1,然后根据从表s1中取出的每行数据中的no值,去表s2的索引no上查找满足条件的记录。在形式上,这个过程就跟我们写程序时的嵌套查询类似,并且可以用上被驱动表的索引,所以我们称之为“IndexNested-Loop Join”,简称 NLJ。
它对应的流程图如下所示:

注:

A、这个过程,对驱动表 s1 做了全表扫描,这个过程需要扫描 100 行;而对于每一行 M,根据 no 字段去表 s2 查找,走的是树搜索过程。假如数据都是一一对应的,每次的搜索过程都只扫描一行,也是总共扫描 100 行;所以,整个执行流程总扫描行数是 200。

B、在这种算法中,外层循环(驱动表)的每一行都会用来查找内层循环(被驱动表)中的匹配行,这个过程可以通过索引来完成,一步步地找个后面关联表的匹配行,形成一部分数据就写到 net_buffer里,net_buffer满了就推给客户端,因此整个过程是一个边查询数据边推送的过程,不需要把某一个表的数据暂存,因此不需要使用join_buffer(边查边推是所有数据库都用的方案,这就是为什么查询了一个巨大的表没有占满内存)。

C、NLJ算法时间复杂度:O(N × logM)(N 为驱动表行数,M 为被驱动表行数)。其中 N 对整个算法影响最大,故应该降低 N 的值,也就是用小表做驱动表。

D、使用NLJ 的前提是被驱动表(inner table)的关联字段上有索引(主键、唯一索引或普通二级索引皆可),所以我们要给被驱动表建立合适的索引。

E、NLJ算法是一种非常优秀的方案,99%的情况使用NLJ是非常合适的,但也有极个别的情况MySQL优化器估算会认为NLJ 成本低于 Block Nested-Loop(BNL)或 Hash Join从而不使用NLJ,这种情况这里不再详细说明。

F、不光MySQL,其他数据库如 PostGreSQL、Oracle等也是这种思想。

是否应该用Join

很多人说最好的方法是不用join,但是为什么不用?用和不用有什么区别?对底层细节说不清楚,下面咱一起分析一下。

对于示例中的SQL,假设不使用 join,那我们就只能用单表查询。我们看看上面这条语句的需求,我们只能在程序(java/php/python....)里这样做:

1、执行 select s1.id, s1.name, s1.no from s1 查出表 s1 的数据,这里有 100 行;

2、循环遍历这 100 行数据:从每一行 M 取出字段 no 的值 $M.no;执行select s2.name from s2 where no=$M.no;把返回的结果和 M 构成结果集的一行。

可以看到,在这个查询过程,也是扫描了 200 行,但执行了 101 次查询,就有101个I/O,比直接 join 多了100 次。除此之外,程序里还要自己拼接 SQL 语句和结果,显然还不如直接 join 好。

于是我们得出结论:对于这种join比较少的情况,完全可以放心使用 Join。

尽量不要做太多表关联查询,一旦数据量上来很难优化。

总结

再次说出结论:99%的情况下,我们要通过给被驱动表建索引使 Join 查询使用NLJ,以提升查询效率。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 16:00:29

解读GB/T4857.7-2005:医疗器械包装正弦定频振动测试意义

在医疗器械、生物制药、敷料、疫苗等行业,产品运输过程中的安全性与完整性直接关系到临床使用效果和患者生命安全。GB/T4857.7-2005《包装 运输包装件基本试验 第7部分:正弦定频振动试验方法》作为运输包装振动测试的重要标准,为相关企业提供…

作者头像 李华
网站建设 2026/4/16 12:59:48

选择高效服装管理ERP系统的最佳推荐与比较分析

市场上高效的服装管理ERP系统推荐分析 在选择高效的服装管理ERP系统时,市场上有多款值得关注的产品。其中,艾格文服装ERP以其全面的功能和灵活的定制选项备受推崇。该系统专注于提高服装企业的运营效率,提供全链条的管理解决方案。为了使决策…

作者头像 李华
网站建设 2026/4/18 8:51:17

10分钟入门A2A协议

在本教程中,你将使用 Python SDK 探索一个简单的“回显”(echo:就是直接返回一个固定的字符串)A2A 服务器。这将帮助你了解 A2A 服务器的基本概念和核心组件。 本教程分为以下步骤: 环境设置(Setup&#xf…

作者头像 李华
网站建设 2026/4/18 8:41:16

安达发|煤炭行业APS高级排产:开启高效生产新时代

在煤炭行业,生产的高效与精准一直是企业追求的目标。而APS高级排产,正成为实现这一目标的关键利器。那么,APS高级排产究竟是什么?它又能为煤炭行业带来怎样的变革呢?APS高级排产,煤炭生产的智能大脑APS高级…

作者头像 李华