性能检查与优化评估 performance-gate

这个技能用于验证软件应用的性能,包括检测N+1查询问题、评估可扩展性和分析代码复杂度。它帮助开发者在代码审查中捕捉性能反模式,确保应用在大规模时保持高效和稳定。关键词:性能优化,N+1查询检测,可扩展性评估,代码复杂度分析,软件性能检查。

DevOps 0 次安装 0 次浏览 更新于 3/8/2026

name: performance-gate description: 验证性能,包括N+1查询检测、可扩展性评估和复杂性分析。警告:在/own:done流程中触发此门。

门 4:性能审查

“代码能运行是第一步。代码能扩展是第二步。”

目的

此门在性能问题造成麻烦之前捕捉性能反模式。重点是明显问题,而不是微优化。

门状态

  • 通过 — 无明显性能问题
  • 警告 — 发现可能在大规模时导致问题的迹象

门问题

问题 1:可扩展性

“当有10,000个项目时会发生什么?1,000,000个时呢?”

关注点:

  • 对数据增长的意识
  • 大数据集的分页
  • 高效的数据结构
  • 无不必要的循环

问题 2:查询效率

“此操作进行多少次数据库查询?”

关注点:

  • 无N+1查询
  • 适当的批量操作
  • 查询列上的索引
  • 查询成本的意识

问题 3:重新渲染意识(前端)

“当此状态改变时,哪些组件会重新渲染?”

关注点:

  • 对渲染触发器的意识
  • 适当的记忆化
  • 状态放置优化
  • 渲染中无昂贵的计算

性能检查清单

数据库操作

  • [ ] 无N+1查询(循环中的查询)
  • [ ] 列表端点的分页
  • [ ] 频繁查询列的索引
  • [ ] 只选择需要的列(非SELECT *)

前端渲染

  • [ ] 昂贵计算使用useMemo
  • [ ] 事件处理程序在需要时使用useCallback
  • [ ] 大列表使用虚拟化
  • [ ] 重组件懒加载

API 和网络

  • [ ] 响应负载最小化
  • [ ] 大数据分页
  • [ ] 适当的缓存头
  • [ ] 无冗余API调用

一般

  • [ ] 无嵌套循环(O(n²))除非有正当理由
  • [ ] 无阻塞操作
  • [ ] 清理间隔/超时
  • [ ] 合理的内存使用

响应模板

如果通过

✅ 性能门:通过

性能考虑看起来良好:
- 数据获取高效
- 无明显的N+1模式
- 适当的分页就位

进入下一门...

如果警告

⚠️ 性能门:警告

发现 [X] 个性能问题:

**问题 1:[N+1查询 / 低效循环]**
位置:`file.ts:42`
问题:“这进行了 [N] 次查询。我们可以批处理成1次吗?”

**问题 2:[缺少分页]**
位置:`file.ts:88`
问题:“有100,000条记录时会发生什么?”

**问题 3:[昂贵渲染]**
位置:`Component.tsx:15`
问题:“这需要在每次渲染时重新计算吗?”

这些问题现在可能无关紧要,但随着应用增长会变成问题。

常见检查问题

1. N+1查询问题

❌ const users = await User.findAll();
   for (const user of users) {
     user.posts = await Post.findByUserId(user.id);
   }
   // 1 + N 次查询!

✅ const users = await User.findAll({
     include: [{ model: Post }]
   });
   // 1 次查询带有 JOIN

2. 获取所有内容

❌ // 返回10,000个用户,每个50个字段
   GET /api/users

✅ // 分页,只使用需要的字段
   GET /api/users?page=1&limit=20&fields=id,name,email

3. 昂贵的渲染计算

❌ function UserList({ users }) {
     // 每次渲染都运行
     const sorted = users.sort((a, b) => a.name.localeCompare(b.name));
     return <ul>{sorted.map(...)}</ul>;
   }

✅ function UserList({ users }) {
     const sorted = useMemo(
       () => [...users].sort((a, b) => a.name.localeCompare(b.name)),
       [users]
     );
     return <ul>{sorted.map(...)}</ul>;
   }

4. 内联函数导致重新渲染

❌ function Parent() {
     return <Child onClick={() => doSomething()} />;
     // 每次渲染新函数 → 子组件重新渲染
   }

✅ function Parent() {
     const handleClick = useCallback(() => doSomething(), []);
     return <Child onClick={handleClick} />;
   }

5. 缺少清理

❌ useEffect(() => {
     const interval = setInterval(fetchData, 5000);
     // 内存泄漏!永远运行
   }, []);

✅ useEffect(() => {
     const interval = setInterval(fetchData, 5000);
     return () => clearInterval(interval);
   }, []);

苏格拉底式性能问题

代替直接指出修复,询问:

  1. “此端点为100个用户执行多少次查询?”
  2. “如果我添加10,000个更多项目,什么会崩溃?”
  3. “这个数组在每次渲染时都会重新排序吗?”
  4. “当组件卸载时,什么清除这个间隔?”
  5. “我们需要此表的所有50列吗?”

大O快速参考

模式 复杂度 10,000个项目 关注级别
映射查找 O(1) 1 操作 良好
单循环 O(n) 10,000 操作 通常良好
嵌套循环 O(n²) 100M 操作 警告
三重循环 O(n³) 1T 操作 关键

性能红旗

红旗 问题 原因
循环中的查询 “我们能批处理吗?” N+1问题
无分页 “大规模时呢?” 内存/时间爆炸
SELECT * “需要所有字段吗?” 浪费带宽
setInterval无清理 “什么清除这个?” 内存泄漏
JSX中的内联对象/函数 “新引用?” 不必要的重新渲染
渲染中的Array.sort() “缓存了吗?” 每次渲染都运行

何时不必担心

并非所有都需要优化:

  • 小数据集:不要为20个项目分页
  • 罕见操作:一次性管理员脚本可以慢
  • 原型阶段:先让它工作
  • 微优化:专注于算法,而不是forforEach

此门是关于捕捉明显问题,而不是微优化。