ai-engineering-init 1.1.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.
- package/.claude/agents/code-reviewer.md +139 -0
- package/.claude/agents/project-manager.md +159 -0
- package/.claude/audio/completed.wav +0 -0
- package/.claude/commands/add-todo.md +255 -0
- package/.claude/commands/check.md +210 -0
- package/.claude/commands/crud.md +454 -0
- package/.claude/commands/dev.md +503 -0
- package/.claude/commands/init-docs.md +681 -0
- package/.claude/commands/next.md +251 -0
- package/.claude/commands/progress.md +242 -0
- package/.claude/commands/start.md +199 -0
- package/.claude/commands/sync.md +307 -0
- package/.claude/commands/update-status.md +428 -0
- package/.claude/docs/Mixin/344/275/277/347/224/250/346/214/207/345/215/227.md +299 -0
- package/.claude/docs/README.md +167 -0
- package/.claude/docs//345/211/215/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +599 -0
- package/.claude/docs//345/220/216/347/253/257/345/274/200/345/217/221/346/214/207/345/215/227.md +726 -0
- package/.claude/docs//345/267/245/344/275/234/346/265/201/345/274/200/345/217/221/346/214/207/345/215/227.md +714 -0
- package/.claude/docs//345/267/245/345/205/267/347/261/273/344/275/277/347/224/250/346/214/207/345/215/227.md +463 -0
- package/.claude/docs//346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/350/247/204/350/214/203.md +390 -0
- package/.claude/docs//346/226/260/345/212/237/350/203/275/345/274/200/345/217/221/346/265/201/347/250/213/350/247/204/350/214/203.md +688 -0
- package/.claude/docs//346/226/260/351/241/271/347/233/256/345/274/200/345/217/221/346/265/201/347/250/213.md +365 -0
- package/.claude/docs//346/241/206/346/236/266/350/257/264/346/230/216.md +393 -0
- package/.claude/docs//350/267/257/347/224/261/351/205/215/347/275/256/346/214/207/345/215/227.md +246 -0
- package/.claude/framework-config.json +73 -0
- package/.claude/hooks/pre-tool-use.js +117 -0
- package/.claude/hooks/skill-forced-eval.js +167 -0
- package/.claude/hooks/stop.js +58 -0
- package/.claude/settings.json +41 -0
- package/.claude/skills/add-skill/SKILL.md +352 -0
- package/.claude/skills/api-development/SKILL.md +560 -0
- package/.claude/skills/architecture-design/SKILL.md +756 -0
- package/.claude/skills/backend-annotations/SKILL.md +674 -0
- package/.claude/skills/banana-image/CHANGELOG.md +37 -0
- package/.claude/skills/banana-image/README.md +146 -0
- package/.claude/skills/banana-image/SKILL.md +164 -0
- package/.claude/skills/banana-image/assets/logo.png +0 -0
- package/.claude/skills/banana-image/references/advanced-usage.md +189 -0
- package/.claude/skills/banana-image/scripts/apply_template.py +125 -0
- package/.claude/skills/banana-image/scripts/banana_image_exec.ts +412 -0
- package/.claude/skills/banana-image/scripts/batch_prep.py +82 -0
- package/.claude/skills/banana-image/scripts/package-lock.json +1437 -0
- package/.claude/skills/banana-image/scripts/package.json +18 -0
- package/.claude/skills/banana-image/scripts/requirements.txt +10 -0
- package/.claude/skills/banana-image/templates/poster.json +22 -0
- package/.claude/skills/banana-image/templates/product.json +17 -0
- package/.claude/skills/banana-image/templates/social.json +22 -0
- package/.claude/skills/banana-image/templates/thumbnail.json +17 -0
- package/.claude/skills/brainstorm/SKILL.md +648 -0
- package/.claude/skills/bug-detective/SKILL.md +1206 -0
- package/.claude/skills/code-patterns/SKILL.md +590 -0
- package/.claude/skills/collaborating-with-codex/SKILL.md +174 -0
- package/.claude/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
- package/.claude/skills/collaborating-with-gemini/SKILL.md +194 -0
- package/.claude/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
- package/.claude/skills/crud-development/SKILL.md +649 -0
- package/.claude/skills/data-permission/SKILL.md +599 -0
- package/.claude/skills/database-ops/SKILL.md +407 -0
- package/.claude/skills/error-handler/SKILL.md +371 -0
- package/.claude/skills/file-oss-management/SKILL.md +863 -0
- package/.claude/skills/git-workflow/SKILL.md +375 -0
- package/.claude/skills/json-serialization/SKILL.md +357 -0
- package/.claude/skills/leniu-api-development/SKILL.md +803 -0
- package/.claude/skills/leniu-architecture-design/SKILL.md +598 -0
- package/.claude/skills/leniu-backend-annotations/SKILL.md +664 -0
- package/.claude/skills/leniu-code-patterns/SKILL.md +365 -0
- package/.claude/skills/leniu-crud-development/SKILL.md +1110 -0
- package/.claude/skills/leniu-data-permission/SKILL.md +256 -0
- package/.claude/skills/leniu-database-ops/SKILL.md +426 -0
- package/.claude/skills/leniu-error-handler/SKILL.md +462 -0
- package/.claude/skills/leniu-java-amount-handling/SKILL.md +461 -0
- package/.claude/skills/leniu-java-code-style/SKILL.md +510 -0
- package/.claude/skills/leniu-java-concurrent/SKILL.md +400 -0
- package/.claude/skills/leniu-java-entity/SKILL.md +751 -0
- package/.claude/skills/leniu-java-export/SKILL.md +560 -0
- package/.claude/skills/leniu-java-logging/SKILL.md +832 -0
- package/.claude/skills/leniu-java-mq/SKILL.md +338 -0
- package/.claude/skills/leniu-java-mybatis/SKILL.md +640 -0
- package/.claude/skills/leniu-java-report-query-param/SKILL.md +291 -0
- package/.claude/skills/leniu-java-task/SKILL.md +367 -0
- package/.claude/skills/leniu-java-total-line/SKILL.md +195 -0
- package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
- package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
- package/.claude/skills/leniu-mealtime/SKILL.md +215 -0
- package/.claude/skills/leniu-redis-cache/SKILL.md +316 -0
- package/.claude/skills/leniu-security-guard/SKILL.md +520 -0
- package/.claude/skills/leniu-utils-toolkit/SKILL.md +380 -0
- package/.claude/skills/openspec-apply-change/SKILL.md +156 -0
- package/.claude/skills/openspec-archive-change/SKILL.md +114 -0
- package/.claude/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.claude/skills/openspec-continue-change/SKILL.md +118 -0
- package/.claude/skills/openspec-explore/SKILL.md +290 -0
- package/.claude/skills/openspec-ff-change/SKILL.md +101 -0
- package/.claude/skills/openspec-new-change/SKILL.md +74 -0
- package/.claude/skills/openspec-onboard/SKILL.md +529 -0
- package/.claude/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.claude/skills/openspec-verify-change/SKILL.md +168 -0
- package/.claude/skills/performance-doctor/SKILL.md +627 -0
- package/.claude/skills/project-navigator/SKILL.md +305 -0
- package/.claude/skills/redis-cache/SKILL.md +839 -0
- package/.claude/skills/scheduled-jobs/SKILL.md +633 -0
- package/.claude/skills/security-guard/SKILL.md +748 -0
- package/.claude/skills/sms-mail/SKILL.md +766 -0
- package/.claude/skills/social-login/SKILL.md +668 -0
- package/.claude/skills/store-pc/SKILL.md +366 -0
- package/.claude/skills/task-tracker/SKILL.md +307 -0
- package/.claude/skills/tech-decision/SKILL.md +393 -0
- package/.claude/skills/tenant-management/SKILL.md +603 -0
- package/.claude/skills/test-development/SKILL.md +755 -0
- package/.claude/skills/ui-pc/SKILL.md +438 -0
- package/.claude/skills/utils-toolkit/SKILL.md +615 -0
- package/.claude/skills/websocket-sse/SKILL.md +716 -0
- package/.claude/skills/workflow-engine/SKILL.md +676 -0
- package/.claude/templates//345/276/205/345/212/236/346/270/205/345/215/225/346/250/241/346/235/277.md +56 -0
- package/.claude/templates//351/234/200/346/261/202/346/226/207/346/241/243/346/250/241/346/235/277.md +85 -0
- package/.claude/templates//351/241/271/347/233/256/347/212/266/346/200/201/346/250/241/346/235/277.md +43 -0
- package/.codex/skills/add-skill/SKILL.md +352 -0
- package/.codex/skills/add-todo/SKILL.md +269 -0
- package/.codex/skills/api-development/SKILL.md +693 -0
- package/.codex/skills/architecture-design/SKILL.md +628 -0
- package/.codex/skills/backend-annotations/SKILL.md +664 -0
- package/.codex/skills/banana-image/CHANGELOG.md +37 -0
- package/.codex/skills/banana-image/README.md +146 -0
- package/.codex/skills/banana-image/SKILL.md +164 -0
- package/.codex/skills/banana-image/assets/logo.png +0 -0
- package/.codex/skills/banana-image/references/advanced-usage.md +189 -0
- package/.codex/skills/banana-image/scripts/apply_template.py +125 -0
- package/.codex/skills/banana-image/scripts/banana_image_exec.ts +412 -0
- package/.codex/skills/banana-image/scripts/batch_prep.py +82 -0
- package/.codex/skills/banana-image/scripts/package-lock.json +1437 -0
- package/.codex/skills/banana-image/scripts/package.json +18 -0
- package/.codex/skills/banana-image/scripts/requirements.txt +10 -0
- package/.codex/skills/banana-image/templates/poster.json +22 -0
- package/.codex/skills/banana-image/templates/product.json +17 -0
- package/.codex/skills/banana-image/templates/social.json +22 -0
- package/.codex/skills/banana-image/templates/thumbnail.json +17 -0
- package/.codex/skills/brainstorm/SKILL.md +648 -0
- package/.codex/skills/bug-detective/SKILL.md +1206 -0
- package/.codex/skills/check/SKILL.md +367 -0
- package/.codex/skills/code-patterns/SKILL.md +442 -0
- package/.codex/skills/collaborating-with-codex/SKILL.md +174 -0
- package/.codex/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
- package/.codex/skills/collaborating-with-gemini/SKILL.md +194 -0
- package/.codex/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
- package/.codex/skills/crud/SKILL.md +265 -0
- package/.codex/skills/crud-development/SKILL.md +637 -0
- package/.codex/skills/data-permission/SKILL.md +591 -0
- package/.codex/skills/database-ops/SKILL.md +553 -0
- package/.codex/skills/dev/SKILL.md +187 -0
- package/.codex/skills/error-handler/SKILL.md +361 -0
- package/.codex/skills/file-oss-management/SKILL.md +863 -0
- package/.codex/skills/git-workflow/SKILL.md +375 -0
- package/.codex/skills/init-docs/SKILL.md +194 -0
- package/.codex/skills/json-serialization/SKILL.md +357 -0
- package/.codex/skills/leniu-api-development/SKILL.md +803 -0
- package/.codex/skills/leniu-architecture-design/SKILL.md +594 -0
- package/.codex/skills/leniu-backend-annotations/SKILL.md +662 -0
- package/.codex/skills/leniu-code-patterns/SKILL.md +365 -0
- package/.codex/skills/leniu-crud-development/SKILL.md +1110 -0
- package/.codex/skills/leniu-data-permission/SKILL.md +256 -0
- package/.codex/skills/leniu-database-ops/SKILL.md +426 -0
- package/.codex/skills/leniu-error-handler/SKILL.md +462 -0
- package/.codex/skills/leniu-java-amount-handling/SKILL.md +461 -0
- package/.codex/skills/leniu-java-code-style/SKILL.md +510 -0
- package/.codex/skills/leniu-java-concurrent/SKILL.md +400 -0
- package/.codex/skills/leniu-java-entity/SKILL.md +751 -0
- package/.codex/skills/leniu-java-export/SKILL.md +560 -0
- package/.codex/skills/leniu-java-logging/SKILL.md +832 -0
- package/.codex/skills/leniu-java-mq/SKILL.md +338 -0
- package/.codex/skills/leniu-java-mybatis/SKILL.md +640 -0
- package/.codex/skills/leniu-java-report-query-param/SKILL.md +291 -0
- package/.codex/skills/leniu-java-task/SKILL.md +367 -0
- package/.codex/skills/leniu-java-total-line/SKILL.md +195 -0
- package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
- package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
- package/.codex/skills/leniu-mealtime/SKILL.md +215 -0
- package/.codex/skills/leniu-redis-cache/SKILL.md +316 -0
- package/.codex/skills/leniu-security-guard/SKILL.md +520 -0
- package/.codex/skills/leniu-utils-toolkit/SKILL.md +378 -0
- package/.codex/skills/next/SKILL.md +137 -0
- package/.codex/skills/openspec-apply-change/SKILL.md +156 -0
- package/.codex/skills/openspec-archive-change/SKILL.md +114 -0
- package/.codex/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.codex/skills/openspec-continue-change/SKILL.md +118 -0
- package/.codex/skills/openspec-explore/SKILL.md +290 -0
- package/.codex/skills/openspec-ff-change/SKILL.md +101 -0
- package/.codex/skills/openspec-new-change/SKILL.md +74 -0
- package/.codex/skills/openspec-onboard/SKILL.md +529 -0
- package/.codex/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.codex/skills/openspec-verify-change/SKILL.md +168 -0
- package/.codex/skills/performance-doctor/SKILL.md +627 -0
- package/.codex/skills/progress/SKILL.md +193 -0
- package/.codex/skills/project-navigator/SKILL.md +286 -0
- package/.codex/skills/redis-cache/SKILL.md +829 -0
- package/.codex/skills/scheduled-jobs/SKILL.md +633 -0
- package/.codex/skills/security-guard/SKILL.md +739 -0
- package/.codex/skills/sms-mail/SKILL.md +766 -0
- package/.codex/skills/social-login/SKILL.md +668 -0
- package/.codex/skills/start/SKILL.md +154 -0
- package/.codex/skills/store-pc/SKILL.md +491 -0
- package/.codex/skills/sync/SKILL.md +149 -0
- package/.codex/skills/task-tracker/SKILL.md +307 -0
- package/.codex/skills/tech-decision/SKILL.md +393 -0
- package/.codex/skills/tenant-management/SKILL.md +603 -0
- package/.codex/skills/test-development/SKILL.md +755 -0
- package/.codex/skills/ui-pc/SKILL.md +475 -0
- package/.codex/skills/update-status/SKILL.md +159 -0
- package/.codex/skills/utils-toolkit/SKILL.md +593 -0
- package/.codex/skills/websocket-sse/SKILL.md +716 -0
- package/.codex/skills/workflow-engine/SKILL.md +676 -0
- package/.cursor/agents/code-reviewer.md +139 -0
- package/.cursor/agents/project-manager.md +159 -0
- package/.cursor/commands/opsx-apply.md +152 -0
- package/.cursor/commands/opsx-archive.md +157 -0
- package/.cursor/commands/opsx-bulk-archive.md +242 -0
- package/.cursor/commands/opsx-continue.md +114 -0
- package/.cursor/commands/opsx-explore.md +174 -0
- package/.cursor/commands/opsx-ff.md +94 -0
- package/.cursor/commands/opsx-new.md +69 -0
- package/.cursor/commands/opsx-onboard.md +525 -0
- package/.cursor/commands/opsx-sync.md +134 -0
- package/.cursor/commands/opsx-verify.md +164 -0
- package/.cursor/mcp.json +22 -0
- package/.cursor/skills/add-skill/SKILL.md +352 -0
- package/.cursor/skills/api-development/SKILL.md +560 -0
- package/.cursor/skills/architecture-design/SKILL.md +756 -0
- package/.cursor/skills/backend-annotations/SKILL.md +674 -0
- package/.cursor/skills/banana-image/CHANGELOG.md +37 -0
- package/.cursor/skills/banana-image/README.md +146 -0
- package/.cursor/skills/banana-image/SKILL.md +164 -0
- package/.cursor/skills/banana-image/assets/logo.png +0 -0
- package/.cursor/skills/banana-image/references/advanced-usage.md +189 -0
- package/.cursor/skills/banana-image/scripts/apply_template.py +125 -0
- package/.cursor/skills/banana-image/scripts/banana_image_exec.ts +412 -0
- package/.cursor/skills/banana-image/scripts/batch_prep.py +82 -0
- package/.cursor/skills/banana-image/scripts/package-lock.json +1437 -0
- package/.cursor/skills/banana-image/scripts/package.json +18 -0
- package/.cursor/skills/banana-image/scripts/requirements.txt +10 -0
- package/.cursor/skills/banana-image/templates/poster.json +22 -0
- package/.cursor/skills/banana-image/templates/product.json +17 -0
- package/.cursor/skills/banana-image/templates/social.json +22 -0
- package/.cursor/skills/banana-image/templates/thumbnail.json +17 -0
- package/.cursor/skills/brainstorm/SKILL.md +648 -0
- package/.cursor/skills/bug-detective/SKILL.md +1206 -0
- package/.cursor/skills/code-patterns/SKILL.md +590 -0
- package/.cursor/skills/collaborating-with-codex/SKILL.md +174 -0
- package/.cursor/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
- package/.cursor/skills/collaborating-with-gemini/SKILL.md +194 -0
- package/.cursor/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
- package/.cursor/skills/crud-development/SKILL.md +649 -0
- package/.cursor/skills/data-permission/SKILL.md +599 -0
- package/.cursor/skills/database-ops/SKILL.md +407 -0
- package/.cursor/skills/error-handler/SKILL.md +371 -0
- package/.cursor/skills/file-oss-management/SKILL.md +863 -0
- package/.cursor/skills/git-workflow/SKILL.md +375 -0
- package/.cursor/skills/json-serialization/SKILL.md +357 -0
- package/.cursor/skills/leniu-api-development/SKILL.md +803 -0
- package/.cursor/skills/leniu-architecture-design/SKILL.md +598 -0
- package/.cursor/skills/leniu-backend-annotations/SKILL.md +664 -0
- package/.cursor/skills/leniu-code-patterns/SKILL.md +365 -0
- package/.cursor/skills/leniu-crud-development/SKILL.md +1110 -0
- package/.cursor/skills/leniu-data-permission/SKILL.md +256 -0
- package/.cursor/skills/leniu-database-ops/SKILL.md +426 -0
- package/.cursor/skills/leniu-error-handler/SKILL.md +462 -0
- package/.cursor/skills/leniu-java-amount-handling/SKILL.md +461 -0
- package/.cursor/skills/leniu-java-code-style/SKILL.md +510 -0
- package/.cursor/skills/leniu-java-concurrent/SKILL.md +400 -0
- package/.cursor/skills/leniu-java-entity/SKILL.md +751 -0
- package/.cursor/skills/leniu-java-export/SKILL.md +560 -0
- package/.cursor/skills/leniu-java-logging/SKILL.md +832 -0
- package/.cursor/skills/leniu-java-mq/SKILL.md +338 -0
- package/.cursor/skills/leniu-java-mybatis/SKILL.md +640 -0
- package/.cursor/skills/leniu-java-report-query-param/SKILL.md +291 -0
- package/.cursor/skills/leniu-java-task/SKILL.md +367 -0
- package/.cursor/skills/leniu-java-total-line/SKILL.md +195 -0
- package/.cursor/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
- package/.cursor/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
- package/.cursor/skills/leniu-mealtime/SKILL.md +215 -0
- package/.cursor/skills/leniu-redis-cache/SKILL.md +316 -0
- package/.cursor/skills/leniu-security-guard/SKILL.md +520 -0
- package/.cursor/skills/leniu-utils-toolkit/SKILL.md +380 -0
- package/.cursor/skills/openspec-apply-change/SKILL.md +156 -0
- package/.cursor/skills/openspec-archive-change/SKILL.md +114 -0
- package/.cursor/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.cursor/skills/openspec-continue-change/SKILL.md +118 -0
- package/.cursor/skills/openspec-explore/SKILL.md +290 -0
- package/.cursor/skills/openspec-ff-change/SKILL.md +101 -0
- package/.cursor/skills/openspec-new-change/SKILL.md +74 -0
- package/.cursor/skills/openspec-onboard/SKILL.md +529 -0
- package/.cursor/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.cursor/skills/openspec-verify-change/SKILL.md +168 -0
- package/.cursor/skills/performance-doctor/SKILL.md +627 -0
- package/.cursor/skills/project-navigator/SKILL.md +305 -0
- package/.cursor/skills/redis-cache/SKILL.md +839 -0
- package/.cursor/skills/scheduled-jobs/SKILL.md +633 -0
- package/.cursor/skills/security-guard/SKILL.md +748 -0
- package/.cursor/skills/sms-mail/SKILL.md +766 -0
- package/.cursor/skills/social-login/SKILL.md +668 -0
- package/.cursor/skills/store-pc/SKILL.md +366 -0
- package/.cursor/skills/task-tracker/SKILL.md +307 -0
- package/.cursor/skills/tech-decision/SKILL.md +393 -0
- package/.cursor/skills/tenant-management/SKILL.md +603 -0
- package/.cursor/skills/test-development/SKILL.md +755 -0
- package/.cursor/skills/ui-pc/SKILL.md +438 -0
- package/.cursor/skills/utils-toolkit/SKILL.md +615 -0
- package/.cursor/skills/websocket-sse/SKILL.md +716 -0
- package/.cursor/skills/workflow-engine/SKILL.md +676 -0
- package/AGENTS.md +669 -0
- package/CLAUDE.md +205 -0
- package/README.md +205 -0
- package/bin/index.js +179 -0
- package/init.sh +178 -0
- package/package.json +27 -0
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: performance-doctor
|
|
3
|
+
description: |
|
|
4
|
+
后端性能问题诊断与优化。包含 SQL 优化、缓存策略、批量操作优化、接口优化、日志分析。
|
|
5
|
+
|
|
6
|
+
触发场景:
|
|
7
|
+
- 接口响应慢
|
|
8
|
+
- SQL 慢查询优化
|
|
9
|
+
- 缓存策略设计
|
|
10
|
+
- 分页查询优化
|
|
11
|
+
- N+1 查询问题
|
|
12
|
+
- 批量操作超时
|
|
13
|
+
- 多租户查询优化
|
|
14
|
+
|
|
15
|
+
触发词:性能优化、慢查询、SQL优化、索引优化、缓存、Redis缓存、N+1、分页优化、EXPLAIN、响应慢、批量操作、多租户优化
|
|
16
|
+
|
|
17
|
+
注意:如果是排查功能性 Bug(代码报错、逻辑错误),请使用 bug-detective。
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# 后端性能优化指南
|
|
21
|
+
|
|
22
|
+
> ⚠️ **本项目规范**:本文档基于项目实际代码编写,所有 API 和类名均已验证。标记 `🔴 本项目规范` 的部分必须严格遵守。
|
|
23
|
+
|
|
24
|
+
## 目录
|
|
25
|
+
|
|
26
|
+
- [性能问题诊断流程](#性能问题诊断流程)
|
|
27
|
+
- [MyBatis-Plus 查询优化](#1-mybatis-plus-查询优化)
|
|
28
|
+
- [SQL 优化](#2-sql-优化)
|
|
29
|
+
- [缓存优化](#3-缓存优化)
|
|
30
|
+
- [多租户优化](#4-多租户优化)
|
|
31
|
+
- [批量操作优化](#5-批量操作优化)
|
|
32
|
+
- [接口优化](#6-接口优化)
|
|
33
|
+
- [性能日志分析](#7-性能日志分析)
|
|
34
|
+
- [性能指标与监控工具](#8-性能指标与监控工具)
|
|
35
|
+
- [常见性能问题速查](#常见性能问题速查)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 性能问题诊断流程
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
1. 定位问题
|
|
43
|
+
├─ 接口层面?→ 检查响应时间、调用链路
|
|
44
|
+
├─ SQL 层面?→ 检查慢查询、执行计划
|
|
45
|
+
└─ 缓存层面?→ 检查命中率、过期策略
|
|
46
|
+
|
|
47
|
+
2. 分析原因
|
|
48
|
+
├─ 使用工具测量(p6spy/Arthas/日志分析)
|
|
49
|
+
└─ 找出瓶颈点
|
|
50
|
+
|
|
51
|
+
3. 实施优化
|
|
52
|
+
└─ 针对性优化(索引/缓存/批量处理)
|
|
53
|
+
|
|
54
|
+
4. 验证效果
|
|
55
|
+
└─ 对比优化前后(响应时间/SQL 耗时)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 1. MyBatis-Plus 查询优化
|
|
61
|
+
|
|
62
|
+
> **实际代码位置**:`ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java:62-73`
|
|
63
|
+
|
|
64
|
+
本项目使用 **MyBatis-Plus 原生的 LambdaQueryWrapper 和 LambdaUpdateWrapper**,没有自定义封装类。
|
|
65
|
+
|
|
66
|
+
### 🔴 查询构建规范(本项目规范)
|
|
67
|
+
|
|
68
|
+
```java
|
|
69
|
+
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
70
|
+
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
71
|
+
|
|
72
|
+
// ✅ 正确:本项目标准写法
|
|
73
|
+
private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
|
|
74
|
+
Map<String, Object> params = bo.getParams();
|
|
75
|
+
LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
|
|
76
|
+
|
|
77
|
+
// 条件判断+查询条件
|
|
78
|
+
lqw.eq(bo.getDeptId() != null, TestDemo::getDeptId, bo.getDeptId());
|
|
79
|
+
lqw.eq(bo.getUserId() != null, TestDemo::getUserId, bo.getUserId());
|
|
80
|
+
lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
|
|
81
|
+
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
|
|
82
|
+
TestDemo::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
|
|
83
|
+
|
|
84
|
+
return lqw;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 使用
|
|
88
|
+
@Override
|
|
89
|
+
public TableDataInfo<TestDemoVo> queryPageList(TestDemoBo bo, PageQuery pageQuery) {
|
|
90
|
+
LambdaQueryWrapper<TestDemo> lqw = buildQueryWrapper(bo);
|
|
91
|
+
Page<TestDemoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
|
92
|
+
return TableDataInfo.build(result);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 🔴 更新构建规范(本项目规范)
|
|
97
|
+
|
|
98
|
+
> **实际代码位置**:`ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java:383-404`
|
|
99
|
+
|
|
100
|
+
```java
|
|
101
|
+
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
102
|
+
|
|
103
|
+
// ✅ 正确:使用 LambdaUpdateWrapper
|
|
104
|
+
@Override
|
|
105
|
+
public int updateUserStatus(Long userId, String status) {
|
|
106
|
+
return baseMapper.update(null,
|
|
107
|
+
new LambdaUpdateWrapper<SysUser>()
|
|
108
|
+
.set(SysUser::getStatus, status)
|
|
109
|
+
.eq(SysUser::getUserId, userId));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ✅ 正确:带条件的更新(只有非null值才设置)
|
|
113
|
+
@Override
|
|
114
|
+
public int updateUserProfile(SysUserBo user) {
|
|
115
|
+
return baseMapper.update(null,
|
|
116
|
+
new LambdaUpdateWrapper<SysUser>()
|
|
117
|
+
.set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName())
|
|
118
|
+
.set(SysUser::getPhonenumber, user.getPhonenumber())
|
|
119
|
+
.set(SysUser::getEmail, user.getEmail())
|
|
120
|
+
.set(SysUser::getSex, user.getSex())
|
|
121
|
+
.eq(SysUser::getUserId, user.getUserId()));
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 智能条件的性能优势
|
|
126
|
+
|
|
127
|
+
MyBatis-Plus 的条件构造器支持条件判断,自动忽略不满足的条件:
|
|
128
|
+
|
|
129
|
+
```java
|
|
130
|
+
// ❌ 传统写法(每个条件都要判断,代码冗长)
|
|
131
|
+
LambdaQueryWrapper<Xxx> wrapper = new LambdaQueryWrapper<>();
|
|
132
|
+
if (bo.getId() != null) {
|
|
133
|
+
wrapper.eq(Xxx::getId, bo.getId());
|
|
134
|
+
}
|
|
135
|
+
if (StringUtils.isNotBlank(bo.getName())) {
|
|
136
|
+
wrapper.like(Xxx::getName, bo.getName());
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ✅ MyBatis-Plus 条件构造(自动处理 null 和空字符串)
|
|
140
|
+
LambdaQueryWrapper<Xxx> lqw = Wrappers.lambdaQuery();
|
|
141
|
+
lqw.eq(bo.getId() != null, Xxx::getId, bo.getId()); // null 自动跳过
|
|
142
|
+
lqw.like(StringUtils.isNotBlank(bo.getName()), Xxx::getName, bo.getName()); // 空字符串自动跳过
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 批量更新优化
|
|
146
|
+
|
|
147
|
+
```java
|
|
148
|
+
// ✅ 推荐:使用 Db 工具类批量更新
|
|
149
|
+
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
|
150
|
+
|
|
151
|
+
public void batchUpdateStatus(List<Long> ids, String status) {
|
|
152
|
+
List<Xxx> list = ids.stream()
|
|
153
|
+
.map(id -> {
|
|
154
|
+
Xxx entity = new Xxx();
|
|
155
|
+
entity.setId(id);
|
|
156
|
+
entity.setStatus(status);
|
|
157
|
+
return entity;
|
|
158
|
+
})
|
|
159
|
+
.toList();
|
|
160
|
+
Db.updateBatchById(list);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ❌ 避免:循环单个更新(N次数据库访问)
|
|
164
|
+
for (Long id : ids) {
|
|
165
|
+
baseMapper.update(null,
|
|
166
|
+
new LambdaUpdateWrapper<Xxx>()
|
|
167
|
+
.set(Xxx::getStatus, status)
|
|
168
|
+
.eq(Xxx::getId, id));
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 2. SQL 优化
|
|
175
|
+
|
|
176
|
+
### 慢查询分析
|
|
177
|
+
|
|
178
|
+
```sql
|
|
179
|
+
-- 开启慢查询日志(MySQL 层面)
|
|
180
|
+
SET GLOBAL slow_query_log = 'ON';
|
|
181
|
+
SET GLOBAL long_query_time = 1; -- 超过1秒记录
|
|
182
|
+
|
|
183
|
+
-- 查看慢查询配置
|
|
184
|
+
SHOW VARIABLES LIKE '%slow_query%';
|
|
185
|
+
|
|
186
|
+
-- 本项目使用 p6spy 进行 SQL 监控(非 Druid)
|
|
187
|
+
-- 开发环境默认开启:spring.datasource.dynamic.p6spy: true
|
|
188
|
+
-- 生产环境默认关闭:spring.datasource.dynamic.p6spy: false
|
|
189
|
+
-- SQL 日志输出在 ./logs/console.log 中,格式包含 Consume Time
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 执行计划分析
|
|
193
|
+
|
|
194
|
+
```sql
|
|
195
|
+
-- 使用 EXPLAIN 分析
|
|
196
|
+
EXPLAIN SELECT * FROM sys_user WHERE dept_id = 100;
|
|
197
|
+
|
|
198
|
+
-- 关注字段
|
|
199
|
+
-- type: ALL(全表扫描) < index < range < ref < const
|
|
200
|
+
-- rows: 扫描行数,越少越好
|
|
201
|
+
-- Extra: Using filesort(需优化)、Using temporary(需优化)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 索引优化
|
|
205
|
+
|
|
206
|
+
```sql
|
|
207
|
+
-- ✅ 好的索引设计
|
|
208
|
+
CREATE INDEX idx_dept_status ON sys_user(dept_id, status);
|
|
209
|
+
|
|
210
|
+
-- 索引使用原则
|
|
211
|
+
-- 1. 最左前缀原则
|
|
212
|
+
-- 2. 避免在索引列上使用函数
|
|
213
|
+
-- 3. 避免 != 和 NOT IN
|
|
214
|
+
-- 4. 注意索引选择性
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### N+1 查询优化
|
|
218
|
+
|
|
219
|
+
```java
|
|
220
|
+
// ❌ 不好:N+1 查询
|
|
221
|
+
for (Order order : orders) {
|
|
222
|
+
User user = userMapper.selectById(order.getUserId()); // 每次循环都查询
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ✅ 好的:批量查询 + Map 映射
|
|
226
|
+
List<Long> userIds = orders.stream()
|
|
227
|
+
.map(Order::getUserId)
|
|
228
|
+
.distinct()
|
|
229
|
+
.toList();
|
|
230
|
+
Map<Long, User> userMap = userMapper.selectBatchIds(userIds).stream()
|
|
231
|
+
.collect(Collectors.toMap(User::getUserId, Function.identity()));
|
|
232
|
+
|
|
233
|
+
for (Order order : orders) {
|
|
234
|
+
User user = userMap.get(order.getUserId()); // O(1) 查找
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 3. 缓存优化
|
|
241
|
+
|
|
242
|
+
> **实际代码位置**:`ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java`
|
|
243
|
+
|
|
244
|
+
本项目封装了 `RedisUtils`,基于 Redisson 实现。
|
|
245
|
+
|
|
246
|
+
### 基础缓存操作
|
|
247
|
+
|
|
248
|
+
```java
|
|
249
|
+
import org.dromara.common.redis.utils.RedisUtils;
|
|
250
|
+
import java.time.Duration;
|
|
251
|
+
|
|
252
|
+
// 设置缓存(带过期时间)
|
|
253
|
+
RedisUtils.setCacheObject("user:" + id, userVo, Duration.ofMinutes(30));
|
|
254
|
+
|
|
255
|
+
// 获取缓存
|
|
256
|
+
UserVo cached = RedisUtils.getCacheObject("user:" + id);
|
|
257
|
+
|
|
258
|
+
// 删除缓存
|
|
259
|
+
RedisUtils.deleteObject("user:" + id);
|
|
260
|
+
|
|
261
|
+
// 检查是否存在
|
|
262
|
+
boolean exists = RedisUtils.isExistsObject("user:" + id);
|
|
263
|
+
|
|
264
|
+
// 设置过期时间
|
|
265
|
+
RedisUtils.expire("user:" + id, Duration.ofHours(1));
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Spring Cache 注解(推荐)
|
|
269
|
+
|
|
270
|
+
```java
|
|
271
|
+
// 使用 Spring Cache 注解(更简洁)
|
|
272
|
+
@Cacheable(value = "user", key = "#id")
|
|
273
|
+
public UserVo getById(Long id) {
|
|
274
|
+
return MapstructUtils.convert(baseMapper.selectById(id), UserVo.class);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@CacheEvict(value = "user", key = "#bo.id")
|
|
278
|
+
public int update(UserBo bo) {
|
|
279
|
+
return baseMapper.updateById(MapstructUtils.convert(bo, User.class));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// 缓存穿透防护(缓存 null 值)
|
|
283
|
+
@Cacheable(value = "user", key = "#id", unless = "#result == null")
|
|
284
|
+
public UserVo getById(Long id) {
|
|
285
|
+
// ...
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
> ⚠️ **重要警告**:`@Cacheable` 方法**禁止返回不可变集合**(`List.of()`、`Set.of()`、`Map.of()`),会导致 Redis 反序列化失败!必须使用可变集合:
|
|
290
|
+
> ```java
|
|
291
|
+
> // ❌ 错误
|
|
292
|
+
> return List.of("1", "2");
|
|
293
|
+
> // ✅ 正确
|
|
294
|
+
> return new ArrayList<>(List.of("1", "2"));
|
|
295
|
+
> ```
|
|
296
|
+
|
|
297
|
+
### 分布式锁(Lock4j)
|
|
298
|
+
|
|
299
|
+
> **实际代码位置**:`ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/RedisLockController.java:47-67`
|
|
300
|
+
|
|
301
|
+
本项目使用 **Lock4j + Redisson** 实现分布式锁。
|
|
302
|
+
|
|
303
|
+
```java
|
|
304
|
+
import com.baomidou.lock.LockInfo;
|
|
305
|
+
import com.baomidou.lock.LockTemplate;
|
|
306
|
+
import com.baomidou.lock.annotation.Lock4j;
|
|
307
|
+
import com.baomidou.lock.executor.RedissonLockExecutor;
|
|
308
|
+
|
|
309
|
+
// 方式1:注解方式(推荐)
|
|
310
|
+
@Lock4j(keys = {"#key"}, expire = 60000, acquireTimeout = 3000)
|
|
311
|
+
public void doSomething(String key) {
|
|
312
|
+
// 业务逻辑
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 方式2:编程方式
|
|
316
|
+
@Autowired
|
|
317
|
+
private LockTemplate lockTemplate;
|
|
318
|
+
|
|
319
|
+
public void doSomething(String key) {
|
|
320
|
+
final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
|
|
321
|
+
if (null == lockInfo) {
|
|
322
|
+
throw new ServiceException("业务处理中,请稍后再试");
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
// 业务逻辑
|
|
326
|
+
} finally {
|
|
327
|
+
lockTemplate.releaseLock(lockInfo);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### 限流控制
|
|
333
|
+
|
|
334
|
+
```java
|
|
335
|
+
import org.redisson.api.RateType;
|
|
336
|
+
|
|
337
|
+
// 限流:每分钟最多 100 次请求
|
|
338
|
+
long remaining = RedisUtils.rateLimiter(
|
|
339
|
+
"api:rate:" + userId,
|
|
340
|
+
RateType.OVERALL, // 全局限流
|
|
341
|
+
100, // 允许的请求数
|
|
342
|
+
60 // 时间窗口(秒)
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
if (remaining < 0) {
|
|
346
|
+
throw new ServiceException("请求过于频繁,请稍后再试");
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 本地缓存(Caffeine)
|
|
351
|
+
|
|
352
|
+
```java
|
|
353
|
+
import com.github.benmanes.caffeine.cache.Cache;
|
|
354
|
+
import com.github.benmanes.caffeine.cache.Caffeine;
|
|
355
|
+
|
|
356
|
+
// 对于热点数据,可以使用本地缓存减少 Redis 访问
|
|
357
|
+
private final Cache<Long, UserVo> localCache = Caffeine.newBuilder()
|
|
358
|
+
.maximumSize(1000) // 最大缓存数量
|
|
359
|
+
.expireAfterWrite(5, TimeUnit.MINUTES) // 写入后5分钟过期
|
|
360
|
+
.build();
|
|
361
|
+
|
|
362
|
+
public UserVo getUserWithLocalCache(Long id) {
|
|
363
|
+
return localCache.get(id, key -> {
|
|
364
|
+
// 本地缓存未命中,从 Redis 或数据库获取
|
|
365
|
+
UserVo cached = RedisUtils.getCacheObject("user:" + id);
|
|
366
|
+
if (cached != null) {
|
|
367
|
+
return cached;
|
|
368
|
+
}
|
|
369
|
+
return MapstructUtils.convert(baseMapper.selectById(id), UserVo.class);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## 4. 多租户优化
|
|
377
|
+
|
|
378
|
+
本项目使用 `TenantEntity` 实现多租户,需要注意索引设计。
|
|
379
|
+
|
|
380
|
+
```sql
|
|
381
|
+
-- ✅ 必须为 tenant_id 建立索引
|
|
382
|
+
CREATE INDEX idx_tenant_id ON xxx_table(tenant_id);
|
|
383
|
+
|
|
384
|
+
-- ✅ 复合索引:租户 + 常用查询条件
|
|
385
|
+
CREATE INDEX idx_tenant_status ON xxx_table(tenant_id, status);
|
|
386
|
+
CREATE INDEX idx_tenant_create_time ON xxx_table(tenant_id, create_time);
|
|
387
|
+
CREATE INDEX idx_tenant_user ON xxx_table(tenant_id, create_by);
|
|
388
|
+
|
|
389
|
+
-- ⚠️ 注意:多租户查询会自动添加 tenant_id 条件
|
|
390
|
+
-- 确保复合索引的第一列是 tenant_id
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## 5. 批量操作优化
|
|
396
|
+
|
|
397
|
+
```java
|
|
398
|
+
// ✅ 推荐:分批插入(每批500条)
|
|
399
|
+
public void batchInsert(List<Xxx> list) {
|
|
400
|
+
int batchSize = 500;
|
|
401
|
+
for (int i = 0; i < list.size(); i += batchSize) {
|
|
402
|
+
int end = Math.min(i + batchSize, list.size());
|
|
403
|
+
baseMapper.insertBatch(list.subList(i, end));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ❌ 避免:一次性插入大量数据
|
|
408
|
+
baseMapper.insertBatch(hugeList); // 可能超时或内存溢出
|
|
409
|
+
|
|
410
|
+
// ✅ 批量更新优化
|
|
411
|
+
@Transactional(rollbackFor = Exception.class)
|
|
412
|
+
public void batchUpdate(List<XxxBo> list) {
|
|
413
|
+
int batchSize = 500;
|
|
414
|
+
for (int i = 0; i < list.size(); i += batchSize) {
|
|
415
|
+
int end = Math.min(i + batchSize, list.size());
|
|
416
|
+
List<Xxx> batch = list.subList(i, end).stream()
|
|
417
|
+
.map(bo -> MapstructUtils.convert(bo, Xxx.class))
|
|
418
|
+
.toList();
|
|
419
|
+
Db.updateBatchById(batch);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## 6. 接口优化
|
|
427
|
+
|
|
428
|
+
```java
|
|
429
|
+
// ❌ 不好:返回所有字段
|
|
430
|
+
public List<Order> listOrders() {
|
|
431
|
+
return baseMapper.selectList(null);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ✅ 好的:只返回需要的字段(使用 VO)
|
|
435
|
+
public List<OrderSimpleVo> listOrders() {
|
|
436
|
+
return baseMapper.selectList(null).stream()
|
|
437
|
+
.map(o -> MapstructUtils.convert(o, OrderSimpleVo.class))
|
|
438
|
+
.toList();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// ✅ 好的:分页查询(本项目标准写法)
|
|
442
|
+
public TableDataInfo<OrderVo> pageOrders(OrderBo bo, PageQuery pageQuery) {
|
|
443
|
+
LambdaQueryWrapper<Order> lqw = buildQueryWrapper(bo);
|
|
444
|
+
Page<OrderVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
|
445
|
+
return TableDataInfo.build(result);
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## 7. 性能日志分析
|
|
452
|
+
|
|
453
|
+
> **实际代码位置**:`ruoyi-admin/src/main/resources/logback-plus.xml:52-72`
|
|
454
|
+
|
|
455
|
+
### 日志文件位置
|
|
456
|
+
|
|
457
|
+
| 环境 | 日志文件 | 说明 |
|
|
458
|
+
|------|---------|------|
|
|
459
|
+
| **开发环境** | `./logs/console.log` | 每次启动清空,包含所有日志和 SQL 日志 |
|
|
460
|
+
| 生产环境 | `./logs/sys-*.log` | 分级别按天滚动,保留60天 |
|
|
461
|
+
|
|
462
|
+
> ⚠️ **AI 应优先分析开发环境日志**(`./logs/console.log`),因为:
|
|
463
|
+
> - 只包含当前启动的日志,范围小,易分析
|
|
464
|
+
> - 包含完整的 SQL 执行时间和性能数据
|
|
465
|
+
> - 启动时自动清空,避免历史日志干扰
|
|
466
|
+
|
|
467
|
+
### AI 自动读取触发条件
|
|
468
|
+
|
|
469
|
+
当用户描述以下性能问题时,**AI 必须主动 Read ./logs/console.log**:
|
|
470
|
+
|
|
471
|
+
| 触发场景 | 关键词 | 日志分析重点 |
|
|
472
|
+
|---------|--------|-------------|
|
|
473
|
+
| 1. 接口响应慢 | "接口慢"、"响应时间长"、"超时" | SQL 执行时间、慢查询(>200ms)、N+1 查询 |
|
|
474
|
+
| 2. SQL 性能问题 | "SQL慢"、"查询慢"、"数据库慢" | p6spy 日志中的 Consume Time、SQL 语句 |
|
|
475
|
+
| 3. 内存或 CPU 高 | "内存占用"、"CPU高"、"卡顿" | OutOfMemoryError、GC 日志、线程池满 |
|
|
476
|
+
| 4. 频繁报错 | "一直报错"、"很多错误" | ERROR 级别日志、异常堆栈、出现频率 |
|
|
477
|
+
| 5. 启动慢 | "启动慢"、"启动时间长" | 应用启动时间、Bean 初始化耗时 |
|
|
478
|
+
|
|
479
|
+
### 日志格式识别规则
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
日期时间 [请求ID] [线程] 级别 日志记录器 - 消息内容
|
|
483
|
+
|
|
484
|
+
示例1(普通日志):
|
|
485
|
+
2026-01-08 22:12:10 [req-123] [http-nio-8080-exec-1] INFO o.d.system.controller.SysUserController - 查询用户列表
|
|
486
|
+
|
|
487
|
+
示例2(SQL 日志 - p6spy):
|
|
488
|
+
2026-01-08 22:12:10 [req-123] [http-nio-8080-exec-1] INFO p6spy - Consume Time:245 ms 2026-01-08 22:12:10
|
|
489
|
+
Execute SQL:SELECT * FROM sys_user WHERE tenant_id = '000000' AND del_flag = '0'
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### 常见性能分析场景
|
|
493
|
+
|
|
494
|
+
#### 场景 1:分析慢 SQL(最常用)
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
# 找出所有执行时间 > 200ms 的 SQL
|
|
498
|
+
grep "Consume Time" ./logs/console.log | grep -E "Consume Time:[2-9][0-9]{2,}|[0-9]{4,}" | head -20
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**分析要点**:
|
|
502
|
+
- 执行时间是否合理(一般 < 200ms)
|
|
503
|
+
- 是否有索引缺失(WHERE 条件的字段)
|
|
504
|
+
- 是否存在 N+1 查询(循环中重复相同 SQL)
|
|
505
|
+
- 是否查询了不必要的字段(SELECT *)
|
|
506
|
+
|
|
507
|
+
#### 场景 2:统计 SQL 执行次数(发现 N+1)
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
# 统计每种 SQL 的执行次数
|
|
511
|
+
grep "Execute SQL" ./logs/console.log | sed 's/WHERE.*/WHERE .../' | sort | uniq -c | sort -rn | head -20
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
**N+1 特征**:
|
|
515
|
+
- 同一个 SELECT 语句重复出现多次
|
|
516
|
+
- 通常在循环中执行
|
|
517
|
+
- 例如:`SELECT * FROM sys_user WHERE user_id = ?` 出现 100 次
|
|
518
|
+
|
|
519
|
+
#### 场景 3:查找异常和错误
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
# 查找所有 ERROR 日志
|
|
523
|
+
grep "ERROR" ./logs/console.log | tail -50
|
|
524
|
+
|
|
525
|
+
# 查找超时错误
|
|
526
|
+
grep -i "timeout\|timed out" ./logs/console.log
|
|
527
|
+
|
|
528
|
+
# 查找内存溢出
|
|
529
|
+
grep -i "OutOfMemoryError\|heap space" ./logs/console.log
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### 完整分析示例
|
|
533
|
+
|
|
534
|
+
**用户问题**:"接口调用很慢,查询用户列表需要 3 秒"
|
|
535
|
+
|
|
536
|
+
**AI 分析流程**:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
# 1. 读取日志文件
|
|
540
|
+
Read ./logs/console.log
|
|
541
|
+
|
|
542
|
+
# 2. 搜索用户相关的 SQL
|
|
543
|
+
grep "sys_user" ./logs/console.log | grep "Consume Time"
|
|
544
|
+
|
|
545
|
+
# 3. 找到慢查询
|
|
546
|
+
# 输出:Consume Time:2845 ms ... Execute SQL:SELECT * FROM sys_user WHERE status = '0'
|
|
547
|
+
|
|
548
|
+
# 4. 分析问题
|
|
549
|
+
# - 执行时间 2845ms(远超 200ms 阈值)
|
|
550
|
+
# - SELECT * 查询所有字段
|
|
551
|
+
# - WHERE status = '0' 可能缺少索引
|
|
552
|
+
|
|
553
|
+
# 5. 给出优化建议
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
**AI 输出**:
|
|
557
|
+
```
|
|
558
|
+
性能分析结果:
|
|
559
|
+
|
|
560
|
+
1. 慢查询定位
|
|
561
|
+
- SQL: SELECT * FROM sys_user WHERE status = '0'
|
|
562
|
+
- 执行时间: 2845ms(严重超标,应 < 200ms)
|
|
563
|
+
|
|
564
|
+
2. 问题原因
|
|
565
|
+
- status 字段缺少索引,导致全表扫描
|
|
566
|
+
- 查询所有字段(SELECT *),增加数据传输量
|
|
567
|
+
- 未使用缓存
|
|
568
|
+
|
|
569
|
+
3. 优化方案(按优先级)
|
|
570
|
+
✅ 立即执行:
|
|
571
|
+
- 添加索引: CREATE INDEX idx_status ON sys_user(status);
|
|
572
|
+
- 只查询需要的字段
|
|
573
|
+
|
|
574
|
+
✅ 短期优化:
|
|
575
|
+
- 添加 Redis 缓存(过期时间 5 分钟)
|
|
576
|
+
- 使用 @Cacheable 注解
|
|
577
|
+
|
|
578
|
+
4. 预期效果
|
|
579
|
+
- 添加索引后: 2845ms → 50ms(预计提升 98%)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## 8. 性能指标与监控工具
|
|
585
|
+
|
|
586
|
+
### 后端性能指标
|
|
587
|
+
|
|
588
|
+
| 指标 | 良好 | 需优化 | 测量工具 |
|
|
589
|
+
|------|------|--------|---------|
|
|
590
|
+
| 接口响应时间 | < 200ms | > 500ms | p6spy/SkyWalking |
|
|
591
|
+
| 数据库查询 | < 50ms | > 200ms | p6spy SQL 日志 |
|
|
592
|
+
| 内存使用 | < 70% | > 85% | Arthas/JVM监控 |
|
|
593
|
+
| CPU 使用 | < 60% | > 80% | 系统监控 |
|
|
594
|
+
|
|
595
|
+
### 后端监控工具
|
|
596
|
+
|
|
597
|
+
| 工具 | 用途 | 使用方式 |
|
|
598
|
+
|------|------|---------|
|
|
599
|
+
| **p6spy** | SQL 监控、执行时间 | 开发环境自动开启(`p6spy: true`) |
|
|
600
|
+
| **Arthas** | JVM 诊断、火焰图 | `java -jar arthas-boot.jar` |
|
|
601
|
+
| **SkyWalking** | 分布式链路追踪 | 需单独部署 |
|
|
602
|
+
| **JProfiler** | 内存分析、CPU分析 | IDE 插件 |
|
|
603
|
+
|
|
604
|
+
### 日志分析命令速查
|
|
605
|
+
|
|
606
|
+
| 命令 | 用途 | 示例 |
|
|
607
|
+
|------|------|------|
|
|
608
|
+
| `wc -l` | 统计行数 | `wc -l console.log` |
|
|
609
|
+
| `grep` | 搜索关键词 | `grep "ERROR" console.log` |
|
|
610
|
+
| `tail -n 100` | 查看最后 100 行 | `tail -n 100 console.log` |
|
|
611
|
+
| `grep -A 5 -B 5` | 显示匹配行的前后 5 行 | `grep -A 5 -B 5 "ERROR" console.log` |
|
|
612
|
+
| `sort \| uniq -c` | 统计重复次数 | `grep "Execute SQL" console.log \| sort \| uniq -c` |
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## 常见性能问题速查
|
|
617
|
+
|
|
618
|
+
| 问题现象 | 可能原因 | 解决方案 |
|
|
619
|
+
|----------|----------|----------|
|
|
620
|
+
| 接口响应慢 | SQL 无索引 | 添加合适索引,使用 EXPLAIN 分析 |
|
|
621
|
+
| 接口响应慢 | N+1 查询 | 改为批量查询 + Map 映射 |
|
|
622
|
+
| 接口响应慢 | 未使用缓存 | 使用 RedisUtils 或 @Cacheable |
|
|
623
|
+
| 分页查询慢 | 深分页问题 | 使用游标分页或限制页码范围 |
|
|
624
|
+
| 批量操作超时 | 一次操作太多数据 | 分批处理,每批 500 条 |
|
|
625
|
+
| 多租户查询慢 | tenant_id 无索引 | 添加 tenant_id 复合索引 |
|
|
626
|
+
| 频繁请求 | 无防抖/缓存 | 添加缓存或接口限流 |
|
|
627
|
+
| 内存溢出 | 大数据量一次加载 | 分批处理、流式处理 |
|