---
name: n-plus-one-query
when: 'N+1查询, 慢查询, 性能问题, 批量查询, 循环查询, SQL优化, 查询优化, JOIN, 延迟加载, eager loading'
scope:
  - backend
products:
  - '*'
id: ka-pattern-n-plus-one-query
asset_kind: pattern
lifecycle_status: active
owner_mechanism_id: mc-backend-pattern
authority: supporting
routes:
  - '*'
primary_triggers:
  - N+1查询
  - 慢查询
  - 性能问题
  - 批量查询
  - 循环查询
  - SQL优化
  - 查询优化
  - JOIN
  - 延迟加载
  - eager loading
priority: P2
specificity: 3
version: 1.1.0
last_reviewed: '2026-06-01'
consumes: []
emits: []
secondary_triggers: []
negative_triggers: []
type: pattern
stages: ["编码与单元测试", "架构设计"]
---

## 决策规则
- 禁止在循环中执行数据库查询（N+1 反模式），必须改为批量查询或 JOIN
- 列表页面展示关联数据必须使用 JOIN 或批量 IN 查询，禁止逐条查询关联实体
- MyBatis 关联查询优先使用 `<collection>` 和 `<association>` 做批量映射，禁止 N+1 嵌套 select
- 分页查询必须使用数据库 LIMIT，禁止内存分页（先查全量再 subList）
- 列表查询字段禁止 SELECT *，只查展示所需字段
- 慢查询（> 500ms）必须有索引支撑或查询计划分析
- 批量 IN 查询参数数量建议不超过 500，超过时分批执行
- 大数据量统计查询必须走汇总表或缓存，禁止实时 COUNT/SUM 全表扫描
- 必须使用 JPA @EntityGraph 或 JOIN FETCH 显式指定关联查询策略
- 批量查询替代循环查询必须使用 IN 子句（WHERE id IN (...)），单次 IN 数量 ≤ 500
- MyBatis 必须使用 <collection> 或 <association> 做关联映射，禁止分段查询
- 列表页面需要关联数据时，先查主表 ID 列表再批量查关联表（两步查询模式）
- 必须在开发和测试环境开启 SQL 日志（hibernate.show_sql），便于发现 N+1 问题

## 验收项
- [AC-01] 无循环内数据库查询
- [AC-02] 列表关联数据使用 JOIN 或批量查询
- [AC-03] 分页使用数据库 LIMIT
- [AC-04] 查询字段非 SELECT *
- [AC-05] 慢查询有索引支撑
- [AC-06] 关联查询使用 JOIN FETCH / @EntityGraph
- [AC-07] 批量查询使用 IN 子句
- [AC-08] 开发环境开启 SQL 日志
