直接回答:N+1 问题指先查一次拿到 N 条主记录,再为每条记录各发一次关联查询,共 N+1 次 SQL。多由 ORM 懒加载触发,循环体里访问关联属性是典型现场。查询次数随数据量线性增长,会迅速拖垮接口。
展开解析:解决思路是把"逐条查"改成"批量查":
- JOIN FETCH / JOIN:一条 SQL 连表取回,适合一对一、一对少量多。
- 批量 IN 查询:先查主表,收集 id 后 WHERE id IN (...) 一次取关联数据,再在内存拼装;Dataloader 模式(GraphQL 场景)本质也是这个。
- 子查询或 EntityGraph 声明式急加载。
- DTO 投影直接在 SQL 层只取需要的列。
检测手段:开发环境打印 SQL 计数,或用 p6spy、Hibernate statistics 统计每个请求的查询数并设阈值告警。
-- 坏:循环里执行 N 次
SELECT * FROM orders WHERE user_id = ?;
-- 好:一次批量
SELECT * FROM orders WHERE user_id IN (1,2,3,...);
追问方向:JOIN 与 IN 分批的取舍(笛卡尔积膨胀)、二级缓存能否缓解。