ai-developer-skill-os 9.3.1 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/.agents/AGENTS.md +73 -40
  2. package/.agents/DEV_PROFILE.md +36 -2
  3. package/.agents/LICENSE +21 -21
  4. package/.agents/docs/ARCHITECTURE.md +56 -120
  5. package/.agents/docs/GOVERNANCE.md +3 -3
  6. package/.agents/docs/SPEC.md +137 -60
  7. package/.agents/docs/VERSIONING.md +25 -57
  8. package/.agents/docs/adr/0005-v10-platform-consolidation.md +50 -0
  9. package/.agents/docs/schemas/learning.schema.yml +22 -57
  10. package/.agents/docs/schemas/skill.schema.yml +116 -161
  11. package/.agents/docs/schemas/workflow.schema.yml +51 -26
  12. package/.agents/docs/skill-classification.md +1 -1
  13. package/.agents/registry/graph.json +193 -346
  14. package/.agents/registry/index.yaml +90 -233
  15. package/.agents/rules/coding.md +30 -12
  16. package/.agents/rules/command-safety.md +20 -9
  17. package/.agents/rules/global.md +234 -28
  18. package/.agents/rules/prompt-compiler.md +170 -0
  19. package/.agents/rules/safety.md +1 -1
  20. package/.agents/rules/security.md +1 -1
  21. package/.agents/rules/skill-quality.md +18 -3
  22. package/.agents/skills/_template/SKILL.md +238 -88
  23. package/.agents/skills/qk-api-data-discovery/SKILL.md +456 -0
  24. package/.agents/skills/qk-api-data-discovery/references/bronze-record-format.md +47 -0
  25. package/.agents/skills/qk-api-data-discovery/references/data-contract-yaml.md +72 -0
  26. package/.agents/skills/qk-api-data-discovery/references/discovery-report-template.md +110 -0
  27. package/.agents/skills/qk-backend-data/SKILL.md +339 -0
  28. package/.agents/skills/qk-bug-resolution/SKILL.md +328 -248
  29. package/.agents/skills/qk-bug-resolution/evals/scorecard.yaml +1 -1
  30. package/.agents/skills/qk-code-cleaner/SKILL.md +399 -0
  31. package/.agents/skills/qk-code-review/SKILL.md +320 -247
  32. package/.agents/skills/qk-code-review/evals/scorecard.yaml +1 -1
  33. package/.agents/skills/qk-code-review/references/ai/{v8-schema-validation.md → schema-validation.md} +2 -2
  34. package/.agents/skills/qk-code-review/references/cross-cutting/async-concurrency-patterns.md +515 -515
  35. package/.agents/skills/qk-code-review/references/cross-cutting/error-handling-principles.md +492 -492
  36. package/.agents/skills/qk-code-review/references/cross-cutting/n-plus-one-queries.md +309 -309
  37. package/.agents/skills/qk-code-review/references/cross-cutting/sql-injection-prevention.md +307 -307
  38. package/.agents/skills/qk-code-review/references/cross-cutting/xss-prevention.md +263 -263
  39. package/.agents/skills/qk-code-review/references/languages/angular.md +768 -768
  40. package/.agents/skills/qk-code-review/references/languages/c.md +890 -890
  41. package/.agents/skills/qk-code-review/references/languages/cpp.md +893 -893
  42. package/.agents/skills/qk-code-review/references/languages/css-less-sass.md +661 -661
  43. package/.agents/skills/qk-code-review/references/languages/django.md +985 -985
  44. package/.agents/skills/qk-code-review/references/languages/fastapi.md +580 -580
  45. package/.agents/skills/qk-code-review/references/languages/go.md +993 -993
  46. package/.agents/skills/qk-code-review/references/languages/java.md +409 -409
  47. package/.agents/skills/qk-code-review/references/languages/java8.md +586 -586
  48. package/.agents/skills/qk-code-review/references/languages/kotlin.md +1018 -1018
  49. package/.agents/skills/qk-code-review/references/languages/nestjs.md +593 -593
  50. package/.agents/skills/qk-code-review/references/languages/php.md +684 -684
  51. package/.agents/skills/qk-code-review/references/languages/python.md +1073 -1073
  52. package/.agents/skills/qk-code-review/references/languages/qt.md +757 -757
  53. package/.agents/skills/qk-code-review/references/languages/react.md +871 -871
  54. package/.agents/skills/qk-code-review/references/languages/ruby.md +964 -964
  55. package/.agents/skills/qk-code-review/references/languages/rust.md +846 -846
  56. package/.agents/skills/qk-code-review/references/languages/svelte.md +1064 -1064
  57. package/.agents/skills/qk-code-review/references/languages/swift.md +936 -936
  58. package/.agents/skills/qk-code-review/references/languages/typescript.md +1016 -1016
  59. package/.agents/skills/qk-code-review/references/languages/vue.md +924 -924
  60. package/.agents/skills/qk-code-review/references/languages/zig.md +440 -440
  61. package/.agents/skills/qk-devops-release/SKILL.md +294 -0
  62. package/.agents/skills/qk-feature-delivery/SKILL.md +331 -247
  63. package/.agents/skills/qk-feature-delivery/evals/scorecard.yaml +1 -1
  64. package/.agents/skills/qk-orchestrator/SKILL.md +286 -152
  65. package/.agents/skills/qk-orchestrator/evals/scorecard.yaml +1 -1
  66. package/.agents/skills/qk-orchestrator/references/routing-table.md +1 -1
  67. package/.agents/skills/qk-product-spec/SKILL.md +262 -0
  68. package/.agents/skills/qk-prompt-compiler/SKILL.md +444 -0
  69. package/.agents/skills/qk-ui-engineer/SKILL.md +286 -0
  70. package/.agents/workflows/_schema.yml +146 -146
  71. package/.agents/workflows/bug-resolution.yml +155 -121
  72. package/.agents/workflows/code-review.yml +127 -93
  73. package/.agents/workflows/context-discovery.yml +128 -94
  74. package/.agents/workflows/documentation.yml +124 -90
  75. package/.agents/workflows/feature-delivery.yml +158 -124
  76. package/.agents/workflows/production-release.yml +207 -173
  77. package/.agents/workflows/prompt-compilation.yml +126 -0
  78. package/.agents/workflows/refactor.yml +136 -102
  79. package/.agents/workflows/security-audit.yml +149 -115
  80. package/.agents/workflows/shared/quality-gate.yml +3 -1
  81. package/.agents/workflows/skin-governance.yml +149 -115
  82. package/.agents/workflows/spec-driven-development.yml +116 -87
  83. package/CHANGELOG.md +77 -0
  84. package/README.md +125 -205
  85. package/bin/install.js +324 -329
  86. package/package.json +68 -74
  87. package/tooling/build-registry.js +226 -208
  88. package/tooling/run-aar.js +55 -126
  89. package/tooling/sync-versions.js +2 -2
  90. package/tooling/validate-graph.js +100 -87
  91. package/tooling/validate-skills.js +32 -14
  92. package/.agents/README.md +0 -90
  93. package/.agents/docs/CHI_TIET_SKILLS.md +0 -126
  94. package/.agents/docs/HUONG_DAN_SU_DUNG.md +0 -120
  95. package/.agents/docs/MIGRATION-CLEANUP-V8.1.3.md +0 -36
  96. package/.agents/docs/MIGRATION-STATUS.md +0 -35
  97. package/.agents/docs/MIGRATION-V8.md +0 -10
  98. package/.agents/docs/ROADMAP-V8.2.md +0 -78
  99. package/.agents/docs/V8-CERTIFICATION.md +0 -27
  100. package/.agents/docs/decisions/ADR-001-v8-migration.md +0 -58
  101. package/.agents/docs/decisions/ADR-002-workflow-separation.md +0 -50
  102. package/.agents/docs/decisions/ADR-003-registry-generated.md +0 -54
  103. package/.agents/docs/decisions/ADR-008-skill-boundary-review.md +0 -27
  104. package/.agents/registry/capability-graph.yml +0 -390
  105. package/.agents/registry/skills-index.yml +0 -305
  106. package/.agents/skills/_template/capability.yaml +0 -34
  107. package/.agents/skills/_template/evals/scorecard.yaml +0 -19
  108. package/.agents/skills/qk-access-policy/SKILL.md +0 -206
  109. package/.agents/skills/qk-access-policy/capability.yaml +0 -23
  110. package/.agents/skills/qk-access-policy/evals/scorecard.yaml +0 -36
  111. package/.agents/skills/qk-agent-observability/SKILL.md +0 -108
  112. package/.agents/skills/qk-agent-observability/capability.yaml +0 -29
  113. package/.agents/skills/qk-agent-observability/evals/scorecard.yaml +0 -29
  114. package/.agents/skills/qk-agent-observability/references/scorecard.yaml +0 -80
  115. package/.agents/skills/qk-ai-builder/SKILL.md +0 -254
  116. package/.agents/skills/qk-ai-builder/capability.yaml +0 -23
  117. package/.agents/skills/qk-ai-builder/evals/scorecard.yaml +0 -30
  118. package/.agents/skills/qk-api-consumer/SKILL.md +0 -256
  119. package/.agents/skills/qk-api-consumer/capability.yaml +0 -21
  120. package/.agents/skills/qk-api-consumer/evals/scorecard.yaml +0 -29
  121. package/.agents/skills/qk-api-lifecycle/SKILL.md +0 -251
  122. package/.agents/skills/qk-api-lifecycle/capability.yaml +0 -23
  123. package/.agents/skills/qk-api-lifecycle/evals/scorecard.yaml +0 -29
  124. package/.agents/skills/qk-bug-resolution/capability.yaml +0 -25
  125. package/.agents/skills/qk-code-review/capability.yaml +0 -23
  126. package/.agents/skills/qk-context-loader/SKILL.md +0 -198
  127. package/.agents/skills/qk-context-loader/capability.yaml +0 -23
  128. package/.agents/skills/qk-context-loader/evals/scorecard.yaml +0 -28
  129. package/.agents/skills/qk-data-engineer/SKILL.md +0 -253
  130. package/.agents/skills/qk-data-lifecycle/SKILL.md +0 -197
  131. package/.agents/skills/qk-data-lifecycle/capability.yaml +0 -23
  132. package/.agents/skills/qk-data-lifecycle/evals/scorecard.yaml +0 -29
  133. package/.agents/skills/qk-db-optimizer/SKILL.md +0 -210
  134. package/.agents/skills/qk-db-optimizer/capability.yaml +0 -22
  135. package/.agents/skills/qk-db-optimizer/evals/scorecard.yaml +0 -28
  136. package/.agents/skills/qk-design-system-engineering/SKILL.md +0 -193
  137. package/.agents/skills/qk-design-system-engineering/capability.yaml +0 -25
  138. package/.agents/skills/qk-design-system-engineering/evals/scorecard.yaml +0 -27
  139. package/.agents/skills/qk-devops-platform/SKILL.md +0 -198
  140. package/.agents/skills/qk-devops-platform/capability.yaml +0 -29
  141. package/.agents/skills/qk-devops-platform/evals/scorecard.yaml +0 -28
  142. package/.agents/skills/qk-docs/SKILL.md +0 -193
  143. package/.agents/skills/qk-docs/capability.yaml +0 -23
  144. package/.agents/skills/qk-docs/evals/scorecard.yaml +0 -27
  145. package/.agents/skills/qk-engineering-standard/SKILL.md +0 -89
  146. package/.agents/skills/qk-engineering-standard/capability.yaml +0 -23
  147. package/.agents/skills/qk-engineering-standard/evals/scorecard.yaml +0 -28
  148. package/.agents/skills/qk-engineering-standard/references/anti-patterns.md +0 -121
  149. package/.agents/skills/qk-engineering-standard/rules/backend.md +0 -122
  150. package/.agents/skills/qk-engineering-standard/rules/database.md +0 -3
  151. package/.agents/skills/qk-engineering-standard/rules/frontend.md +0 -152
  152. package/.agents/skills/qk-engineering-standard/rules/security.md +0 -3
  153. package/.agents/skills/qk-engineering-standard/rules/testing.md +0 -3
  154. package/.agents/skills/qk-fe-api-integration/SKILL.md +0 -704
  155. package/.agents/skills/qk-fe-api-integration/capability.yaml +0 -21
  156. package/.agents/skills/qk-fe-api-integration/evals/scorecard.yaml +0 -29
  157. package/.agents/skills/qk-feature-delivery/capability.yaml +0 -24
  158. package/.agents/skills/qk-frontend-architecture/SKILL.md +0 -127
  159. package/.agents/skills/qk-frontend-architecture/capability.yaml +0 -28
  160. package/.agents/skills/qk-frontend-architecture/evals/scorecard.yaml +0 -28
  161. package/.agents/skills/qk-help/SKILL.md +0 -107
  162. package/.agents/skills/qk-help/capability.yaml +0 -20
  163. package/.agents/skills/qk-help/evals/scorecard.yaml +0 -13
  164. package/.agents/skills/qk-orchestrator/capability.yaml +0 -22
  165. package/.agents/skills/qk-product-specification/SKILL.md +0 -187
  166. package/.agents/skills/qk-product-specification/capability.yaml +0 -27
  167. package/.agents/skills/qk-product-specification/evals/scorecard.yaml +0 -27
  168. package/.agents/skills/qk-production-release/SKILL.md +0 -188
  169. package/.agents/skills/qk-production-release/capability.yaml +0 -27
  170. package/.agents/skills/qk-production-release/evals/scorecard.yaml +0 -28
  171. package/.agents/skills/qk-project-audit/SKILL.md +0 -174
  172. package/.agents/skills/qk-project-bootstrap/SKILL.md +0 -372
  173. package/.agents/skills/qk-project-bootstrap/capability.yaml +0 -23
  174. package/.agents/skills/qk-project-bootstrap/evals/scorecard.yaml +0 -28
  175. package/.agents/skills/qk-project-health/SKILL.md +0 -202
  176. package/.agents/skills/qk-project-health/capability.yaml +0 -23
  177. package/.agents/skills/qk-project-health/evals/scorecard.yaml +0 -27
  178. package/.agents/skills/qk-project-memory/SKILL.md +0 -303
  179. package/.agents/skills/qk-project-memory/capability.yaml +0 -23
  180. package/.agents/skills/qk-project-memory/evals/scorecard.yaml +0 -27
  181. package/.agents/skills/qk-refactor/SKILL.md +0 -243
  182. package/.agents/skills/qk-refactor/capability.yaml +0 -26
  183. package/.agents/skills/qk-refactor/evals/scorecard.yaml +0 -27
  184. package/.agents/skills/qk-security-audit/SKILL.md +0 -280
  185. package/.agents/skills/qk-security-audit/capability.yaml +0 -29
  186. package/.agents/skills/qk-security-audit/evals/scorecard.yaml +0 -27
  187. package/.agents/skills/qk-system-evolution/SKILL.md +0 -625
  188. package/.agents/skills/qk-system-evolution/capability.yaml +0 -24
  189. package/.agents/skills/qk-system-evolution/evals/scorecard.yaml +0 -26
  190. package/.agents/skills/qk-test-engineering/SKILL.md +0 -215
  191. package/.agents/skills/qk-test-engineering/capability.yaml +0 -28
  192. package/.agents/skills/qk-test-engineering/evals/scorecard.yaml +0 -26
  193. package/.agents/skills/qk-ui-audit/SKILL.md +0 -175
  194. package/.agents/skills/qk-ui-audit/capability.yaml +0 -23
  195. package/.agents/skills/qk-ui-audit/evals/scorecard.yaml +0 -26
  196. package/.agents/skills/qk-ui-audit/references/anti-slop-checklist.md +0 -136
  197. package/.agents/skills/qk-ui-builder/SKILL.md +0 -521
  198. package/.agents/skills/qk-ui-builder/capability.yaml +0 -29
  199. package/.agents/skills/qk-ui-builder/evals/scorecard.yaml +0 -26
  200. package/.agents/skills/qk-ui-builder/references/anti-patterns.md +0 -295
  201. package/.agents/skills/qk-ui-builder/references/color.md +0 -115
  202. package/.agents/skills/qk-ui-builder/references/component-cookbook.md +0 -458
  203. package/.agents/skills/qk-ui-builder/references/copy.md +0 -250
  204. package/.agents/skills/qk-ui-builder/references/interaction-and-states.md +0 -115
  205. package/.agents/skills/qk-ui-builder/references/layout-and-space.md +0 -111
  206. package/.agents/skills/qk-ui-builder/references/macrostructures/01-bento-grid.md +0 -48
  207. package/.agents/skills/qk-ui-builder/references/macrostructures/02-long-document.md +0 -50
  208. package/.agents/skills/qk-ui-builder/references/macrostructures/03-marquee-hero.md +0 -51
  209. package/.agents/skills/qk-ui-builder/references/macrostructures/04-stat-led.md +0 -49
  210. package/.agents/skills/qk-ui-builder/references/macrostructures/05-workbench.md +0 -44
  211. package/.agents/skills/qk-ui-builder/references/macrostructures/06-conversational-faq.md +0 -50
  212. package/.agents/skills/qk-ui-builder/references/macrostructures/07-manifesto.md +0 -51
  213. package/.agents/skills/qk-ui-builder/references/macrostructures/08-photographic.md +0 -50
  214. package/.agents/skills/qk-ui-builder/references/macrostructures/09-quote-led.md +0 -50
  215. package/.agents/skills/qk-ui-builder/references/macrostructures/11-catalogue.md +0 -49
  216. package/.agents/skills/qk-ui-builder/references/macrostructures/12-letter.md +0 -49
  217. package/.agents/skills/qk-ui-builder/references/macrostructures/13-index-first.md +0 -49
  218. package/.agents/skills/qk-ui-builder/references/macrostructures/14-narrative-workflow.md +0 -48
  219. package/.agents/skills/qk-ui-builder/references/macrostructures/15-split-studio.md +0 -48
  220. package/.agents/skills/qk-ui-builder/references/macrostructures/16-feature-stack.md +0 -51
  221. package/.agents/skills/qk-ui-builder/references/macrostructures/17-type-specimen.md +0 -48
  222. package/.agents/skills/qk-ui-builder/references/macrostructures/18-portfolio-grid.md +0 -48
  223. package/.agents/skills/qk-ui-builder/references/macrostructures/19-map-diagram.md +0 -50
  224. package/.agents/skills/qk-ui-builder/references/macrostructures/20-ecosystem-index.md +0 -48
  225. package/.agents/skills/qk-ui-builder/references/macrostructures/21-component-playground.md +0 -45
  226. package/.agents/skills/qk-ui-builder/references/macrostructures.md +0 -38
  227. package/.agents/skills/qk-ui-builder/references/motion.md +0 -95
  228. package/.agents/skills/qk-ui-builder/references/responsive.md +0 -115
  229. package/.agents/skills/qk-ui-builder/references/slop-test.md +0 -135
  230. package/.agents/skills/qk-ui-builder/references/structure.md +0 -280
  231. package/.agents/skills/qk-ui-builder/references/themes/atmospheric.md +0 -53
  232. package/.agents/skills/qk-ui-builder/references/themes/carnival.md +0 -52
  233. package/.agents/skills/qk-ui-builder/references/themes/cobalt.md +0 -52
  234. package/.agents/skills/qk-ui-builder/references/themes/editorial.md +0 -52
  235. package/.agents/skills/qk-ui-builder/references/themes/garden.md +0 -52
  236. package/.agents/skills/qk-ui-builder/references/themes/hum.md +0 -52
  237. package/.agents/skills/qk-ui-builder/references/themes/lumen.md +0 -52
  238. package/.agents/skills/qk-ui-builder/references/themes/midnight.md +0 -52
  239. package/.agents/skills/qk-ui-builder/references/themes/modern-minimal.md +0 -52
  240. package/.agents/skills/qk-ui-builder/references/themes/playful.md +0 -52
  241. package/.agents/skills/qk-ui-builder/references/themes/specimen.md +0 -52
  242. package/.agents/skills/qk-ui-builder/references/themes/terminal.md +0 -52
  243. package/.agents/skills/qk-ui-builder/references/typography.md +0 -129
  244. package/.agents/skills/qk-ui-system-builder/SKILL.md +0 -183
  245. package/.agents/skills/qk-ui-system-builder/capability.yaml +0 -25
  246. package/.agents/skills/qk-ui-system-builder/evals/scorecard.yaml +0 -26
  247. package/.agents/skills/qk-upgrade/SKILL.md +0 -301
  248. package/.agents/skills/qk-upgrade/capability.yaml +0 -24
  249. package/.agents/skills/qk-upgrade/evals/scorecard.yaml +0 -26
  250. package/.agents/skills/qk-validation-gate/SKILL.md +0 -88
  251. package/.agents/skills/qk-validation-gate/capability.yaml +0 -23
  252. package/.agents/skills/qk-validation-gate/evals/scorecard.yaml +0 -26
  253. package/.agents/skills/qk-web-quality-gate/SKILL.md +0 -197
  254. package/.agents/skills/qk-web-quality-gate/capability.yaml +0 -24
  255. package/.agents/skills/qk-web-quality-gate/evals/scorecard.yaml +0 -26
  256. package/.agents/workflows/research.yml +0 -75
  257. package/.agents/workflows/skill-evolution.yml +0 -97
  258. package/tooling/fix-refactor.js +0 -8
@@ -1,309 +1,309 @@
1
- # N+1 查询问题 — 跨语言通用指南
2
-
3
- > N+1 查询是 ORM 和数据库访问层最常见的性能反模式。本文档覆盖问题定义、检测方法、通用解决方案和跨语言代码示例。
4
-
5
- ## 目录
6
-
7
- - [问题定义](#问题定义)
8
- - [性能影响](#性能影响)
9
- - [检测方法](#检测方法)
10
- - [通用解决方案](#通用解决方案)
11
- - [语言特定实现](#语言特定实现)
12
- - [Review Checklist](#review-checklist)
13
-
14
- ---
15
-
16
- ## 问题定义
17
-
18
- N+1 查询是指:**1 次查询获取 N 条记录,随后在循环中触发 N 次额外查询**来获取关联数据。
19
-
20
- ```
21
- 请求流程:
22
- 1 query → 获取 N 条主记录
23
- N queries → 每条主记录查一次关联数据
24
- ─────────
25
- Total: 1 + N queries
26
- ```
27
-
28
- ### 危害
29
-
30
- | 问题 | 影响 |
31
- |------|------|
32
- | **查询数量线性增长** | 100 条记录 = 101 条 SQL,1000 条 = 1001 条 |
33
- | **网络延迟叠加** | 每条查询都有往返延迟(RTT),N 次往返 >> 1 次批量查询 |
34
- | **连接池耗尽** | 大量查询占满数据库连接,拖慢整个应用 |
35
- | **难以在开发中发现** | 开发环境数据少,N+1 不明显;生产环境数据量大时性能崩塌 |
36
-
37
- ---
38
-
39
- ## 性能影响
40
-
41
- ### 场景对比:获取 100 个用户及其订单
42
-
43
- | 方案 | SQL 数量 | 延迟(假设 RTT=1ms) | 适用场景 |
44
- |------|----------|---------------------|---------|
45
- | N+1 懒加载 | 101 条 | ~101ms | 极少数据量 |
46
- | Eager loading (JOIN) | 1 条 | ~1ms | 一对多,数据量适中 |
47
- | Eager loading (IN) | 2 条 | ~2ms | 多对多,大数据集 |
48
- | DataLoader / batch | 2 条 | ~2ms | GraphQL / 复杂图查询 |
49
-
50
- ### SQL 数量对比
51
-
52
- ```sql
53
- -- ❌ N+1: 1 + 100 = 101 queries
54
- SELECT * FROM users; -- 1 query
55
- SELECT * FROM orders WHERE user_id = 1; -- query 2
56
- SELECT * FROM orders WHERE user_id = 2; -- query 3
57
- ...
58
- SELECT * FROM orders WHERE user_id = 100; -- query 101
59
-
60
- -- ✅ Batch: 2 queries
61
- SELECT * FROM users;
62
- SELECT * FROM orders WHERE user_id IN (1,2,...,100);
63
- ```
64
-
65
- ---
66
-
67
- ## 检测方法
68
-
69
- ### 1. ORM SQL 日志
70
-
71
- 开启 SQL 日志,在测试或开发环境中观察查询数量:
72
-
73
- ```python
74
- # Django
75
- import logging
76
- logging.getLogger('django.db.backends').setLevel(logging.DEBUG)
77
-
78
- # SQLAlchemy
79
- import logging
80
- logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
81
- ```
82
-
83
- ```java
84
- // Spring Boot application.yml
85
- spring:
86
- jpa:
87
- show-sql: true
88
- properties:
89
- hibernate.format_sql: true
90
- ```
91
-
92
- ```csharp
93
- // EF Core
94
- optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
95
- ```
96
-
97
- ### 2. 查询计数断言
98
-
99
- 在测试中断言 SQL 查询数量:
100
-
101
- ```python
102
- # Django: django-assert-num-queries
103
- from django.test.utils import CaptureQueriesContext
104
- from django.db import connection
105
-
106
- with CaptureQueriesContext(connection) as ctx:
107
- list(User.objects.select_related("profile").all())
108
- assert len(ctx) <= 2 # 预期最多 2 条查询
109
- ```
110
-
111
- ```java
112
- // Hibernate: p6spy 或 datasource-proxy
113
- // 在测试中统计 SQL 执行次数
114
- assertThat(sqlCount).isLessThanOrEqualTo(2);
115
- ```
116
-
117
- ### 3. APM / 数据库监控工具
118
-
119
- - **Django Debug Toolbar** — 实时显示 SQL 数量和时间
120
- - **p6spy** (Java) — JDBC 层拦截,记录所有 SQL
121
- - **MiniProfiler** (.NET) — 页面内嵌 SQL 统计
122
- - **DataDog / New Relic** — 生产环境慢查询告警
123
-
124
- ---
125
-
126
- ## 通用解决方案
127
-
128
- ### 方案 1: Eager Loading(JOIN 预加载)
129
-
130
- 一次 JOIN 查询获取主记录和关联记录。适用于一对一、一对多。
131
-
132
- ### 方案 2: Batch Fetching(IN 子句批量查询)
133
-
134
- 两次查询:主记录 + `WHERE id IN (...)` 批量获取关联记录。适用于多对多、大数据集。
135
-
136
- ### 方案 3: DataLoader Pattern
137
-
138
- 在 GraphQL 或复杂图查询场景中,收集所有需要的 ID,合并为一次批量查询。
139
-
140
- ```
141
- // DataLoader 伪代码
142
- class DataLoader<K, V> {
143
- load(K key) → V // 注册需求,不立即查询
144
- loadAll([K]) → [V] // 合并为一次批量查询
145
- }
146
- ```
147
-
148
- ### 方案 4: Projection(投影)
149
-
150
- 只查询需要的字段,减少数据传输量:
151
-
152
- ```sql
153
- -- ❌ 获取所有列
154
- SELECT * FROM users JOIN profiles ON ...
155
-
156
- -- ✅ 只投影需要的字段
157
- SELECT u.name, p.avatar_url FROM users u JOIN profiles p ON ...
158
- ```
159
-
160
- ---
161
-
162
- ## 语言特定实现
163
-
164
- ### Python / Django
165
-
166
- > 详见 [Django Guide](../django.md#n1-查询优化)
167
-
168
- ```python
169
- # ForeignKey / OneToOne → select_related (SQL JOIN)
170
- books = Book.objects.select_related("publisher")
171
-
172
- # M2M / reverse FK → prefetch_related (2 queries + Python merge)
173
- authors = Author.objects.prefetch_related("books")
174
-
175
- # 嵌套预加载
176
- authors = Author.objects.prefetch_related("books__publisher")
177
-
178
- # Prefetch 对象精细控制
179
- from django.db.models import Prefetch
180
- authors = Author.objects.prefetch_related(
181
- Prefetch("books", queryset=Book.objects.filter(published=True), to_attr="published_books")
182
- )
183
- ```
184
-
185
- ### Python / SQLAlchemy (FastAPI)
186
-
187
- > 详见 [FastAPI Guide](../fastapi.md#database-sessions--n1)
188
-
189
- ```python
190
- from sqlalchemy.orm import selectinload
191
-
192
- # selectinload: IN 子句批量加载(推荐异步场景)
193
- stmt = select(Order).options(selectinload(Order.customer))
194
-
195
- # joinedload: JOIN 加载
196
- stmt = select(Order).options(joinedload(Order.customer))
197
- ```
198
-
199
- ### Java / JPA (Spring Boot)
200
-
201
- > 详见 [Java Guide](../java.md)
202
-
203
- ```java
204
- // ❌ FetchType.EAGER 或循环中触发懒加载
205
- @OneToMany(fetch = FetchType.EAGER) // 危险!
206
-
207
- // ✅ JOIN FETCH
208
- @Query("SELECT u FROM User u JOIN FETCH u.orders")
209
- List<User> findAllWithOrders();
210
-
211
- // ✅ @EntityGraph(声明式)
212
- @EntityGraph(attributePaths = {"orders", "profile"})
213
- List<User> findAll();
214
-
215
- // ✅ @BatchSize(减少 N+1 为 N/batchSize + 1)
216
- @OneToMany
217
- @BatchSize(size = 50)
218
- private List<Order> orders;
219
- ```
220
-
221
- ### C# / EF Core
222
-
223
- > 详见 [C# Guide](../csharp.md)
224
-
225
- ```csharp
226
- // ❌ N+1: foreach 触发懒加载
227
- foreach (var blog in await context.Blogs.ToListAsync())
228
- foreach (var post in blog.Posts) // 每次循环都查询!
229
-
230
- // ✅ Include + ThenInclude
231
- var blogs = await context.Blogs
232
- .Include(b => b.Posts)
233
- .ToListAsync();
234
-
235
- // ✅ 投影(最安全,避免过度获取)
236
- var data = await context.Blogs
237
- .Select(b => new { b.Url, PostTitles = b.Posts.Select(p => p.Title) })
238
- .ToListAsync();
239
- ```
240
-
241
- ### PHP / Laravel / Doctrine
242
-
243
- > 详见 [PHP Guide](../php.md)
244
-
245
- ```php
246
- // ❌ 循环内查询
247
- foreach ($orders as $order) {
248
- $customer = $customerRepo->find($order->customerId);
249
- render($order, $customer);
250
- }
251
-
252
- // ✅ 批量预加载
253
- $customerIds = array_unique(array_map(fn($o) => $o->customerId, $orders));
254
- $customers = $customerRepo->findByIds($customerIds);
255
-
256
- foreach ($orders as $order) {
257
- render($order, $customers[$order->customerId] ?? null);
258
- }
259
-
260
- // Laravel Eloquent: with()
261
- $orders = Order::with('customer')->get();
262
-
263
- // Doctrine: JOIN FETCH
264
- $dql = 'SELECT o, c FROM Order o JOIN o.customer c';
265
- ```
266
-
267
- ### TypeScript / Prisma
268
-
269
- ```typescript
270
- // ❌ N+1
271
- const users = await prisma.user.findMany();
272
- for (const user of users) {
273
- user.posts = await prisma.post.findMany({ where: { userId: user.id } });
274
- }
275
-
276
- // ✅ include(Prisma 自动生成 JOIN 或批量查询)
277
- const users = await prisma.user.findMany({
278
- include: { posts: true },
279
- });
280
-
281
- // ✅ 嵌套 include
282
- const users = await prisma.user.findMany({
283
- include: {
284
- posts: {
285
- include: { comments: true },
286
- },
287
- },
288
- });
289
- ```
290
-
291
- ---
292
-
293
- ## Review Checklist
294
-
295
- ### 检测
296
- - [ ] 开启了 SQL 日志或查询计数监控
297
- - [ ] 测试中有查询数量断言
298
- - [ ] APM 工具配置了 N+1 告警
299
-
300
- ### 修复
301
- - [ ] ForeignKey / OneToOne 关系使用 JOIN eager loading
302
- - [ ] M2M / 反向关系使用 IN 批量预加载
303
- - [ ] 避免在循环中触发数据库查询
304
- - [ ] 使用投影只获取需要的字段
305
-
306
- ### 架构
307
- - [ ] 列表 API 分页,避免一次加载过多记录
308
- - [ ] GraphQL 场景使用 DataLoader
309
- - [ ] 缓存策略(Redis)处理高频读取的关联数据
1
+ # N+1 查询问题 — 跨语言通用指南
2
+
3
+ > N+1 查询是 ORM 和数据库访问层最常见的性能反模式。本文档覆盖问题定义、检测方法、通用解决方案和跨语言代码示例。
4
+
5
+ ## 目录
6
+
7
+ - [问题定义](#问题定义)
8
+ - [性能影响](#性能影响)
9
+ - [检测方法](#检测方法)
10
+ - [通用解决方案](#通用解决方案)
11
+ - [语言特定实现](#语言特定实现)
12
+ - [Review Checklist](#review-checklist)
13
+
14
+ ---
15
+
16
+ ## 问题定义
17
+
18
+ N+1 查询是指:**1 次查询获取 N 条记录,随后在循环中触发 N 次额外查询**来获取关联数据。
19
+
20
+ ```
21
+ 请求流程:
22
+ 1 query → 获取 N 条主记录
23
+ N queries → 每条主记录查一次关联数据
24
+ ─────────
25
+ Total: 1 + N queries
26
+ ```
27
+
28
+ ### 危害
29
+
30
+ | 问题 | 影响 |
31
+ |------|------|
32
+ | **查询数量线性增长** | 100 条记录 = 101 条 SQL,1000 条 = 1001 条 |
33
+ | **网络延迟叠加** | 每条查询都有往返延迟(RTT),N 次往返 >> 1 次批量查询 |
34
+ | **连接池耗尽** | 大量查询占满数据库连接,拖慢整个应用 |
35
+ | **难以在开发中发现** | 开发环境数据少,N+1 不明显;生产环境数据量大时性能崩塌 |
36
+
37
+ ---
38
+
39
+ ## 性能影响
40
+
41
+ ### 场景对比:获取 100 个用户及其订单
42
+
43
+ | 方案 | SQL 数量 | 延迟(假设 RTT=1ms) | 适用场景 |
44
+ |------|----------|---------------------|---------|
45
+ | N+1 懒加载 | 101 条 | ~101ms | 极少数据量 |
46
+ | Eager loading (JOIN) | 1 条 | ~1ms | 一对多,数据量适中 |
47
+ | Eager loading (IN) | 2 条 | ~2ms | 多对多,大数据集 |
48
+ | DataLoader / batch | 2 条 | ~2ms | GraphQL / 复杂图查询 |
49
+
50
+ ### SQL 数量对比
51
+
52
+ ```sql
53
+ -- ❌ N+1: 1 + 100 = 101 queries
54
+ SELECT * FROM users; -- 1 query
55
+ SELECT * FROM orders WHERE user_id = 1; -- query 2
56
+ SELECT * FROM orders WHERE user_id = 2; -- query 3
57
+ ...
58
+ SELECT * FROM orders WHERE user_id = 100; -- query 101
59
+
60
+ -- ✅ Batch: 2 queries
61
+ SELECT * FROM users;
62
+ SELECT * FROM orders WHERE user_id IN (1,2,...,100);
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 检测方法
68
+
69
+ ### 1. ORM SQL 日志
70
+
71
+ 开启 SQL 日志,在测试或开发环境中观察查询数量:
72
+
73
+ ```python
74
+ # Django
75
+ import logging
76
+ logging.getLogger('django.db.backends').setLevel(logging.DEBUG)
77
+
78
+ # SQLAlchemy
79
+ import logging
80
+ logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
81
+ ```
82
+
83
+ ```java
84
+ // Spring Boot application.yml
85
+ spring:
86
+ jpa:
87
+ show-sql: true
88
+ properties:
89
+ hibernate.format_sql: true
90
+ ```
91
+
92
+ ```csharp
93
+ // EF Core
94
+ optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
95
+ ```
96
+
97
+ ### 2. 查询计数断言
98
+
99
+ 在测试中断言 SQL 查询数量:
100
+
101
+ ```python
102
+ # Django: django-assert-num-queries
103
+ from django.test.utils import CaptureQueriesContext
104
+ from django.db import connection
105
+
106
+ with CaptureQueriesContext(connection) as ctx:
107
+ list(User.objects.select_related("profile").all())
108
+ assert len(ctx) <= 2 # 预期最多 2 条查询
109
+ ```
110
+
111
+ ```java
112
+ // Hibernate: p6spy 或 datasource-proxy
113
+ // 在测试中统计 SQL 执行次数
114
+ assertThat(sqlCount).isLessThanOrEqualTo(2);
115
+ ```
116
+
117
+ ### 3. APM / 数据库监控工具
118
+
119
+ - **Django Debug Toolbar** — 实时显示 SQL 数量和时间
120
+ - **p6spy** (Java) — JDBC 层拦截,记录所有 SQL
121
+ - **MiniProfiler** (.NET) — 页面内嵌 SQL 统计
122
+ - **DataDog / New Relic** — 生产环境慢查询告警
123
+
124
+ ---
125
+
126
+ ## 通用解决方案
127
+
128
+ ### 方案 1: Eager Loading(JOIN 预加载)
129
+
130
+ 一次 JOIN 查询获取主记录和关联记录。适用于一对一、一对多。
131
+
132
+ ### 方案 2: Batch Fetching(IN 子句批量查询)
133
+
134
+ 两次查询:主记录 + `WHERE id IN (...)` 批量获取关联记录。适用于多对多、大数据集。
135
+
136
+ ### 方案 3: DataLoader Pattern
137
+
138
+ 在 GraphQL 或复杂图查询场景中,收集所有需要的 ID,合并为一次批量查询。
139
+
140
+ ```
141
+ // DataLoader 伪代码
142
+ class DataLoader<K, V> {
143
+ load(K key) → V // 注册需求,不立即查询
144
+ loadAll([K]) → [V] // 合并为一次批量查询
145
+ }
146
+ ```
147
+
148
+ ### 方案 4: Projection(投影)
149
+
150
+ 只查询需要的字段,减少数据传输量:
151
+
152
+ ```sql
153
+ -- ❌ 获取所有列
154
+ SELECT * FROM users JOIN profiles ON ...
155
+
156
+ -- ✅ 只投影需要的字段
157
+ SELECT u.name, p.avatar_url FROM users u JOIN profiles p ON ...
158
+ ```
159
+
160
+ ---
161
+
162
+ ## 语言特定实现
163
+
164
+ ### Python / Django
165
+
166
+ > 详见 [Django Guide](../django.md#n1-查询优化)
167
+
168
+ ```python
169
+ # ForeignKey / OneToOne → select_related (SQL JOIN)
170
+ books = Book.objects.select_related("publisher")
171
+
172
+ # M2M / reverse FK → prefetch_related (2 queries + Python merge)
173
+ authors = Author.objects.prefetch_related("books")
174
+
175
+ # 嵌套预加载
176
+ authors = Author.objects.prefetch_related("books__publisher")
177
+
178
+ # Prefetch 对象精细控制
179
+ from django.db.models import Prefetch
180
+ authors = Author.objects.prefetch_related(
181
+ Prefetch("books", queryset=Book.objects.filter(published=True), to_attr="published_books")
182
+ )
183
+ ```
184
+
185
+ ### Python / SQLAlchemy (FastAPI)
186
+
187
+ > 详见 [FastAPI Guide](../fastapi.md#database-sessions--n1)
188
+
189
+ ```python
190
+ from sqlalchemy.orm import selectinload
191
+
192
+ # selectinload: IN 子句批量加载(推荐异步场景)
193
+ stmt = select(Order).options(selectinload(Order.customer))
194
+
195
+ # joinedload: JOIN 加载
196
+ stmt = select(Order).options(joinedload(Order.customer))
197
+ ```
198
+
199
+ ### Java / JPA (Spring Boot)
200
+
201
+ > 详见 [Java Guide](../java.md)
202
+
203
+ ```java
204
+ // ❌ FetchType.EAGER 或循环中触发懒加载
205
+ @OneToMany(fetch = FetchType.EAGER) // 危险!
206
+
207
+ // ✅ JOIN FETCH
208
+ @Query("SELECT u FROM User u JOIN FETCH u.orders")
209
+ List<User> findAllWithOrders();
210
+
211
+ // ✅ @EntityGraph(声明式)
212
+ @EntityGraph(attributePaths = {"orders", "profile"})
213
+ List<User> findAll();
214
+
215
+ // ✅ @BatchSize(减少 N+1 为 N/batchSize + 1)
216
+ @OneToMany
217
+ @BatchSize(size = 50)
218
+ private List<Order> orders;
219
+ ```
220
+
221
+ ### C# / EF Core
222
+
223
+ > 详见 [C# Guide](../csharp.md)
224
+
225
+ ```csharp
226
+ // ❌ N+1: foreach 触发懒加载
227
+ foreach (var blog in await context.Blogs.ToListAsync())
228
+ foreach (var post in blog.Posts) // 每次循环都查询!
229
+
230
+ // ✅ Include + ThenInclude
231
+ var blogs = await context.Blogs
232
+ .Include(b => b.Posts)
233
+ .ToListAsync();
234
+
235
+ // ✅ 投影(最安全,避免过度获取)
236
+ var data = await context.Blogs
237
+ .Select(b => new { b.Url, PostTitles = b.Posts.Select(p => p.Title) })
238
+ .ToListAsync();
239
+ ```
240
+
241
+ ### PHP / Laravel / Doctrine
242
+
243
+ > 详见 [PHP Guide](../php.md)
244
+
245
+ ```php
246
+ // ❌ 循环内查询
247
+ foreach ($orders as $order) {
248
+ $customer = $customerRepo->find($order->customerId);
249
+ render($order, $customer);
250
+ }
251
+
252
+ // ✅ 批量预加载
253
+ $customerIds = array_unique(array_map(fn($o) => $o->customerId, $orders));
254
+ $customers = $customerRepo->findByIds($customerIds);
255
+
256
+ foreach ($orders as $order) {
257
+ render($order, $customers[$order->customerId] ?? null);
258
+ }
259
+
260
+ // Laravel Eloquent: with()
261
+ $orders = Order::with('customer')->get();
262
+
263
+ // Doctrine: JOIN FETCH
264
+ $dql = 'SELECT o, c FROM Order o JOIN o.customer c';
265
+ ```
266
+
267
+ ### TypeScript / Prisma
268
+
269
+ ```typescript
270
+ // ❌ N+1
271
+ const users = await prisma.user.findMany();
272
+ for (const user of users) {
273
+ user.posts = await prisma.post.findMany({ where: { userId: user.id } });
274
+ }
275
+
276
+ // ✅ include(Prisma 自动生成 JOIN 或批量查询)
277
+ const users = await prisma.user.findMany({
278
+ include: { posts: true },
279
+ });
280
+
281
+ // ✅ 嵌套 include
282
+ const users = await prisma.user.findMany({
283
+ include: {
284
+ posts: {
285
+ include: { comments: true },
286
+ },
287
+ },
288
+ });
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Review Checklist
294
+
295
+ ### 检测
296
+ - [ ] 开启了 SQL 日志或查询计数监控
297
+ - [ ] 测试中有查询数量断言
298
+ - [ ] APM 工具配置了 N+1 告警
299
+
300
+ ### 修复
301
+ - [ ] ForeignKey / OneToOne 关系使用 JOIN eager loading
302
+ - [ ] M2M / 反向关系使用 IN 批量预加载
303
+ - [ ] 避免在循环中触发数据库查询
304
+ - [ ] 使用投影只获取需要的字段
305
+
306
+ ### 架构
307
+ - [ ] 列表 API 分页,避免一次加载过多记录
308
+ - [ ] GraphQL 场景使用 DataLoader
309
+ - [ ] 缓存策略(Redis)处理高频读取的关联数据