N+1 查询
N+1 Query
高级通用N+1 查询(N+1 Query)是先查出一批记录,再为其中每条记录各查一次关联数据的访问方式。它会让数据库查询次数随记录数增长;识别它后,可以考虑批量查询或预加载关联数据。
你可能会说:「这个列表一多就变慢,能帮我看看是不是给每一行都单独查了一次数据库吗?」
动手看看订单列表的N+1查询
1
用户打开订单页,应用先向数据库查订单列表。假设这次查出了4笔订单。
要点为什么一个列表会触发很多次查询?
- 1
先查列表再逐条查关联
比如你做订单后台,先查出 N 条订单,再遍历订单,逐条查询对应的客户姓名。第一条列表查询加上 N 条客户查询,就是这里的「N+1」。
- 2
查询次数随列表长度增长
列表从 10 条变成 100 条,关联查询也可能从 10 次变成 100 次。即使每次查询都很快,反复发起查询的开销仍可能拖慢页面。
- 3
批量获取需要的关联数据
我们可以先收集这页订单的客户 ID,一次查出相关客户,再按 ID 对应回订单。用 ORM 时,也可以查看它是否支持预加载,并核对实际生成的查询。
容易混淆的地方
N+1 说的是「查一批记录后,又按每条记录重复查关联数据」的查询形态,不等于所有慢查询。若一条查询本身执行很久,我们会先看查询条件和索引。分页能限制每页的 N,但只要逐条查询仍在,它就没有消除 N+1。
什么时候用
适合用
- 做订单、文章或商品列表时,每行都要显示作者、客户或分类等关联信息,可以检查是否出现 N+1。
- AI Agent 改完 ORM 关联读取代码后,可以对照数据库日志,确认列表变长时查询次数是否仍跟着增长。
- 某个列表页在数据量增加后明显变慢,而单条记录页正常,可以把 N+1 作为排查方向之一。
不适合用
- 页面只读取一条记录及其关联数据,没有按列表逐条查询的过程;如果仍然慢,先检查这条查询本身。
- 浏览器为每张卡片分别调用一次 API,但服务端没有重复查数据库;这更适合检查接口设计,考虑批量接口。
- 数据库只执行一条查询却要扫描大量记录;这不是 N+1,更适合检查筛选条件、执行计划和索引。
练一练选一个答案,看看解释
一个项目列表每页显示 50 个项目和各自的负责人姓名。服务端先查项目列表,再循环项目调用 ORM 读取负责人。你会优先怎么处理?
让 AI 帮你做复制给 AI Agent 直接用
请检查项目列表接口是否存在 N+1 查询:找出循环中读取关联数据的代码,对比不同列表长度下的实际数据库查询次数。若存在,请用批量查询或 ORM 预加载改造,保持返回数据不变,并说明如何验证。