CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
构建可扩展系统/课时/N+1 查询问题
RawBlame
课时目录12 节

构建可扩展系统

  1. 01可扩展性是什么
  2. 02CAP 定理
  3. 03无状态服务
  4. 04缓存与读扩展
  5. 05分片策略
  6. 06数据复制
  7. 07N+1 查询问题
  8. 08负载均衡
  9. 09背压与限流
  10. 10负载转移
  11. 11读写分离
  12. 12综合练习

N+1 查询问题

ORM 懒加载陷阱;Join Fetch 与批量查询。

30 分钟

N+1 查询问题

接口响应随列表长度线性变慢,往往是 N+1 查询:一次查主表,再对每条记录各查一次关联表。ORM 懒加载让这个问题隐蔽却致命——是 building-for-scale 中应用层最常见的性能陷阱,与 orm-essentials 紧密相关。

学习目标

  • 在日志/SQL 监控中识别 N+1 模式(1 + N 条 SELECT)
  • 使用 Join Fetch、@EntityGraph、批量 IN 查询三种修复手段
  • 在 DTO 投影与懒加载之间做合理权衡

核心概念

典型场景:查询 100 个订单,再 lazy load 每个订单的用户。

SELECT * FROM orders LIMIT 100;        -- 1 次
SELECT * FROM users WHERE id = ?;      -- ×100 次  → 共 101 次
修复方式JPA 示例思路适用
Join Fetch@Query("SELECT o FROM Order o JOIN FETCH o.user")关联必取、结果集可控
@EntityGraph声明 fetch graph 覆盖 lazy复用 Repository
Batch Size@BatchSize(size=50) 合并 IN 查询不能 JOIN 时
// Hibernate 批量加载:@BatchSize on User
// 101 次 → 约 1 + ceil(100/50) = 3 次

Open Session In View(OSIV) 会掩盖 N+1:页面渲染时才触发 lazy load,把 DB 压力拖到视图层。生产建议关闭 OSIV,在 Service 层显式加载所需关联。

排查工具:Hibernate statistics、P6Spy、Micrometer + JDBC 追踪。压测时对比修复前后 P99。

与扩展

N+1 不解决,加再多 App 实例也只是 并行打爆数据库。配合 scale-cache 缓存热点关联,配合 postgresql-essentials 加索引。

练习

  • 打开一个列表 API,数日志里 SELECT 条数是否等于 1 + 列表长度
  • 用 Join Fetch 改写,对比 EXPLAIN ANALYZE 与响应时间
  • 在团队规范里加一条:PR 必须附 SQL 条数截图或断言

小结

N+1 是 ORM 送的「隐形账单」:一条列表接口就能打满连接池——显式加载或批量查,别靠懒加载碰运气。

加载本节练习…
← 上一课
数据复制
下一课 →负载均衡