性能问题分析工作流
职责范围
本工作流负责找到性能瓶颈的根因:谁、在什么条件下、触发了什么昂贵操作;根因确认后,可据此制定修复方案、实施代码/配置优化并验证效果。
仅「如何发布、灰度、上线」等落地方式不在此工作流内规定,由项目自行决定。
触发词识别
所有触发词均以**「性能」开头**,便于识别与记忆。当用户单独说触发词或使用**「触发词 + 冒号 + 空格 + 具体描述」**形式时,进入本工作流或对应阶段。带冒号时,冒号、空格不限制中英文。
- 「性能分析」 或 「性能分析: xxx」 → 进入本工作流,从阶段 1(性能证据)开始
- 「性能证据」 或 「性能证据: xxx」 → 阶段 1:性能证据
- 「性能定位」 或 「性能定位: xxx」 → 阶段 2:性能定位
- 「性能假设」 或 「性能假设: xxx」 → 阶段 3:性能假设
- 「性能监控」 或 「性能监控: xxx」 → 阶段 4:性能监控
- 「性能优化」 或 「性能优化: xxx」 → 阶段 5:性能优化
- 「性能验证」 或 「性能验证: xxx」 → 阶段 6:性能验证
- 「性能深入」 或 「性能深入: xxx」 → 在阶段 2(性能定位)内继续或深化分析
用户说「性能问题」「卡顿」「很慢」等时,也可视为进入本工作流,从阶段 1(性能证据)开始。
用户提供性能相关日志或 profile 并希望分析时,视为进入本工作流,从阶段 1(性能证据)或阶段 2(性能定位)开始。
通用原则
「先定问题拆链路,再采数据筛瓶颈,下钻推导提假设,控制变量验根因,优化固化成闭环。」
- 数据驱动:先拿数据再推导结论,禁止先假设问题再找佐证;没有复现路径或可分析数据时,先协助用户收集(复现步骤、日志、profile、录屏等)。
- 自上而下:从用户感知的全链路入手,先宏观后微观;先识别「哪里慢」(耗时/阻塞/渲染量等),再追溯「谁触发」「在什么条件下触发」。
- 单一变量、可复现:根因假设要能通过现有数据或少量定向打点得到是/否结论;验证时尽量只改变一个变量,可复现、可证伪,禁止把相关性当因果性。
- 全链路覆盖:分析从问题起点到终点的完整执行链路,不只看局部数据,避免盲人摸象。
- 可开关的正式监控:性能观测应设计为正规的性能监控能力,常驻代码库,通过开关(环境变量、配置项、feature flag 等)控制是否启用。关闭时不输出、不采样,不影响正式产品;需要分析时打开开关即可采集数据,无需临时加码再删。
- 禁止提前优化:只优化「已被数据证实、影响用户体验、超出阈值」的性能问题;没有测量数据支撑的「未来可能的性能问题」,不做预防性优化,避免引入不必要的代码复杂度和维护成本。
阶段流转
正向流程:证据 → 定位 → 假设 → 监控 → 优化 → 验证。
常见跳转:
- 阶段 1 发现缺数据且需建设监控 → 可直接进入阶段 4(性能监控)补充后回到阶段 2。
- 阶段 2 数据充足、瓶颈明确 → 可跳过阶段 3 直接进入阶段 4 或 5。
- 阶段 6 假设被否定 → 回到阶段 2 或 3 重新分析。
- 阶段 6 优化未达标 → 回到阶段 3 重新提假设或阶段 5 调整优化方案。
- 偶现问题在阶段 1 难以采集数据 → 优先进入阶段 4 建设长期监控,等待复现后回到阶段 2。
阶段 1:性能证据(收集证据)
目标
拿到可复现、可分析的性能数据,而不是仅凭描述猜测;并把模糊的「卡、慢」转化为可量化指标与性能基线,明确优化目标与不可突破的红线。
要澄清的信息
- 现象:什么操作下变慢(点击、滚动、输入、拖拽、接口请求等)?慢的表现是什么(卡顿、白屏、转圈、无响应)?尽量量化(如某接口 P99 多少 ms、某操作帧率/耗时),并明确分析边界(问题的起点与终点,避免范围无限扩大)。
- 数据形式:是否已有日志、Performance/CPU profile、网络抓包、自定义打点?若无,需要先确定「在什么环境、用什么方式」能采到数据。
- 复现条件:必现还是偶现?数据量/并发/设备是否有要求?
常见数据来源(按场景选用)
| 场景 | 可选数据来源 |
|---|
| 前端卡顿 / 主线程阻塞 | 控制台日志、Chrome Performance 录制、Long Task / 自定义性能监控 |
| 渲染过多 / 重绘贵 | 框架的渲染统计、React DevTools Profiler、自定义 commit/render 打点 |
| 接口/后端慢 | 网络面板、服务端日志、APM、trace |
| 内存/泄漏 | Heap snapshot、内存趋势图 |
不限定具体技术栈;根据项目实际有的监控和工具选择。
输出
- 明确「当前已有的数据」和「还缺什么」。
- 若缺数据:给出「用户需要执行的操作」和「需要采集的内容」(例如:复现步骤、打开某开关后复现并导出 console/ profile)。
- 建议输出包含:现象(含量化指标)、复现条件、分析边界、当前性能基线、目标阈值或红线;可整理为结构化的「性能问题定义单」便于后续对标。
阶段 2:性能定位(分析定位)
目标
从原始数据里找出异常点(谁在什么时候花了多少时间/资源),并追溯触发链(事件/调用栈/数据流);建立从起点到终点的全链路拓扑,通过双维度数据(时间 + 资源)做瓶颈初筛,把分析范围缩小到 1~2 个核心环节。
分析思路(通用)
- 全链路拓扑:按执行顺序把从用户操作到问题终点的所有环节拆解(代码执行、系统调用、网络/存储等),形成无重叠、全覆盖的拓扑;每个环节可独立计时、有明确输入输出。
- 双维度采集:时间维度(各环节耗时、占全链路比例)+ 资源维度(CPU/内存/IO/网络等),两者缺一不可。
- 瓶颈初筛:按耗时占比排序锁定高耗时环节(如占比 >20%);按资源异常(饱和、持续升、错误率)锁定异常环节;耗时低且资源正常的环节先排除。资源利用率 >70% 是常用的告警线,超过此阈值响应时间往往呈非线性上升,即使绝对值不高也应优先排查。
-
找异常
在时间轴或聚合统计里,找出明显偏大的指标,例如:
- 单次耗时超过阈值的任务/请求(如主线程任务 >50ms、>200ms);
- 单次处理量异常大(如一次更新影响大量节点、一次请求返回巨大 payload);
- 频率异常高(如某事件触发次数远高于预期)。
-
定位置
对每个异常点,确定:
- 发生在哪一层(前端/后端/网络/存储);
- 对应代码或模块(文件、函数、组件、接口);
- 调用栈或事件链(从用户操作或入口到该耗时/耗量点)。
-
建因果链
用「用户操作 → 事件/请求 → 处理函数 → 昂贵操作 → 观测到的指标」串起来,避免只看到现象看不到触发条件。
操作方式
- 对文本日志:用搜索(grep/ripgrep 等)按关键字、耗时、错误码等过滤,再读关键片段和调用栈。
- 对 profile:先看时间线或火焰图上的「宽条」或「高占比」,再定位到具体函数/组件。
- 对代码:用读码、搜索调用关系,配合日志里的栈信息,把「谁在什么条件下调用了谁」理清。
输出
- 异常摘要:哪些地方慢/量大/频率高,对应的大致位置。
- 因果链:用一两句话或简图描述「操作 → … → 瓶颈」。
- 待验证假设(初步方向):列出 1~3 个「可能是根因」的假设方向,并说明每个假设需要什么证据才能成立或否定。精炼与归类在阶段 3(性能假设)完成。
- 核心瓶颈环节清单:1~2 个环节,附耗时占比或资源异常摘要。
阶段 3:性能假设(根因假设)
目标
把「异常点 + 触发链」归纳成可验证的根因假设,并归类到常见性能问题模式,便于后续打点和修复。假设须可验证、可证伪,禁止模糊猜测;可结合业界方法做归类(见下)。
根因推导参考
- USE 方法(资源类瓶颈):从使用率、饱和度、错误率三个维度判断是否为资源饱和导致(如 CPU/内存/IO/网络)。
- RED 方法(执行/请求类瓶颈):从请求率、错误率、耗时三个维度判断是否为执行逻辑或调用链问题。
与下文的「常见模式」表配合使用,不展开具体步骤。
常见模式(技术无关表述)
| 模式 | 特征 | 典型触发方式 |
|---|
| 响应范围过大 | 本应局部更新,却触发全局或大量重算/重渲染 | 粗粒度事件(如「任何变更」)驱动大范围更新;缺少细粒度订阅或 diff |
| 更新未合并/级联 | 一次操作导致多次独立的重计算或重渲染 | 连续多次状态更新未批处理;异步回调中各自触发更新 |
| 高频触发 | 高频率事件每次都会执行昂贵逻辑 | scroll/resize/mousemove 等未节流/防抖;每帧或每次事件都做重算 |
| 积压集中执行 | 主线程忙或队列堆积后,多个延迟任务集中执行 | 多个 throttle/debounce 在同一时刻 fire;定时器/微任务堆积 |
| 资源泄漏 | 内存/连接/句柄持续增长不释放 | 未关闭的连接、未清理的缓存/定时器、未解绑的监听器 |
| 同步阻塞 | 主线程或关键路径被同步操作长时间占用 | 同步 I/O、锁竞争、长事务、大量同步计算 |
| 重复/冗余计算 | 同一结果被反复计算而未缓存 | 缺少 memo/缓存、N+1 查询、重复序列化/反序列化 |
具体项目里可能是「某框架的 setState」「某总线的 emit」「某 RPC」等,但抽象层面都是上述几类。
输出
- 当前最可能的 1~2 个根因假设;每个假设需可验证、可证伪(例如能通过「是否全表扫描」「某变量取值」等得到是/否结论),避免「查询慢是因为 SQL 写得不好」这类不可验证表述。
- 若有多个假设,按「用户感知影响最大 + 验证成本最低」排序,优先验证排名靠前的假设。
- 每个假设对应的验证方式:现有日志能否判断?若不能,需要在哪条路径上增加哪些可开关监控(见阶段 4(性能监控))。根因确认后,可进入阶段 5(性能优化)实施修改,再在阶段 6(性能验证)验证假设及优化效果。
阶段 4:性能监控(建设/补充可开关监控)
目标
在现有数据无法验证假设时,在关键路径上增加可开关的、正规的性能观测。监控逻辑作为正式代码入库,通过开关控制是否启用;关闭时不影响正式产品,需要分析时打开开关即可做性能监控。
何时需要补充监控
- 现有日志/profile 里看不到「谁触发」「在什么条件下触发」或「某变量在当时的取值」。
- 需要对比「假设成立时」与「不成立时」的差异(例如某标志位为 true/false 时的行为)。
- 项目尚未具备针对该路径的开关式性能监控,需要设计并落地。
监控设计原则(通用)
-
开关控制
用运行时开关(环境变量、配置中心、feature flag、本地调试开关等)控制是否启用。生产或正式环境默认关闭,或仅对特定用户/会话开启;分析问题时再打开,避免对正常用户产生开销或噪音。
-
常驻代码、非临时
监控代码是正式能力的一部分,不采用「加完验证再删」的方式。逻辑长期保留,行为完全由开关决定:关则无输出、无采样、无额外开销(或仅极低开销),开则按约定格式输出或上报,便于与现有日志、profile 对齐。
-
可对齐
输出稳定的时间戳(如 performance.now() 或服务端纳秒时间)和位置标识,便于和已有日志、profile 对齐到同一时间轴,做因果与顺序分析。
-
信息够用
每条记录至少包含:位置标识、时间戳、以及能验证/否定假设的少量关键变量(如:是否命中某分支、ID、数量、错误码)。格式可复用项目既有性能日志规范,便于统一分析。
监控点位置选择
- 优先:怀疑的「直接触发点」(例如更新状态的调用、发起请求的调用、执行重算的函数入口)。
- 其次:触发链上的中间节点(例如事件处理入口、回调入口),用于确认调用顺序和频率。
- 再次:昂贵计算的入口/出口,用于确认单次耗时和调用次数。
输出
- 监控点列表(文件:行号或函数/接口名)及每处监控的用途与建议的开关名。
- 用户需要做的操作:如何开启开关、如何复现、如何采集并提供新日志/新 profile。
- 若根因已确认或将在阶段 6 验证,可进入阶段 5(性能优化)实施修改。
阶段 5:性能优化(实施优化)
目标
根据阶段 3 的根因结论(及阶段 4 的监控;若已先在阶段 6 验证过假设,可一并参考),实施代码或配置层面的优化,消除或缓解瓶颈。本阶段只做「针对根因的修改」与修改清单,不在此展开多方案评估或详细任务拆解。
原则
- 针对根因改:改动应对应阶段 3 归纳的根因模式(如缩小响应范围、合并更新、节流/防抖等),避免泛泛优化。
- 利用监控做对比:优先利用阶段 4 的可开关监控,在阶段 6 用相同复现场景做优化前后对比,确认改动生效。
- 控制变量验证:用单一变量实验验证根因后再改;每次优化尽量只动一个根因对应点,便于在阶段 6 验证单个方案收益。
- 分层与性价比:按业务逻辑 → 应用代码 → 框架/依赖 → 系统/硬件从高到低考虑,优先上层;优先「改造成本低、收益高」的方案(如逻辑简化、缓存复用)。
- 耗时占比优先:优先优化耗时占比高的环节;耗时占比 <10% 的环节即便优化 100 倍,整体收益也极为有限,应直接跳过,聚焦真正的主瓶颈。
- 稳定性:不改变业务语义、不引入功能 bug、不产生新的性能副作用。
输出
- 修改清单:文件、位置(函数/模块)、改动要点。
- 建议的验证方式:用何种复现场景、观察哪些指标(与阶段 4 监控对齐),便于阶段 6 做优化效果验证。
阶段 6:性能验证(验证假设与优化效果)
目标
- 用新数据对阶段 3(性能假设)的假设做是/否判断,必要时修正假设或回到阶段 2(性能定位)。
- 若已完成阶段 5(性能优化),则用相同复现场景与阶段 4 的监控数据做前后对比,验证优化是否生效、指标是否达标。
验证方式
- 对每个假设,明确「若成立,日志/profile 里应看到什么」「若不成立,应看到什么」。
- 在新日志中搜索或定位对应模式,看是否符合「成立」的预期。
- 若有时间戳,将不同模块/层的打点对齐到同一时间轴,确认先后顺序和是否在同一任务/请求内。
- 完成优化后,在同一复现路径下对比优化前后关键指标(耗时、调用次数、渲染量等),并说明达标标准。
- 效果验证(完成阶段 5 时适用):
- 基准复测:与优化前相同的复现条件与环境下对比全维度指标。
- 功能回归:确认正常、边界、极限负载场景下功能正常。
- 副作用检查:确认无次生性能问题(如用缓存优化响应时间却导致内存上升)。
- 若有条件:可做线上/灰度验证,用真实流量确认效果。
输出
- 每个假设的结论:成立 / 不成立 / 仍不确定。
- 若仍不确定:说明还缺什么信息,以及下一步是补充可开关监控再采数还是换角度分析。
- 若成立:用一两句话总结根因(谁、在什么条件下、触发了什么),作为后续制定修复方案和实施的输入。
- 若已做阶段 5(性能优化):输出优化前后对比结论与是否达标。
- 闭环固化(可选延伸):验证通过后,建议将关键指标纳入常态化监控与告警;若有 CI/CD,可将性能基准或门禁纳入流水线,防止回退;将本次优化中的指标与最佳实践沉淀到团队规范,形成持续迭代。
- 终止条件:若所有假设已验证、关键指标已达标,或剩余瓶颈的优化性价比过低(改造成本远高于收益),则可结束本轮优化。
输出详细度控制(自适应)
- 问题简单、数据充足:可压缩为「异常点 + 因果链 + 根因结论」。
- 问题复杂、多模块:每个阶段给出简短输出(收集了什么、主要异常、假设、监控点列表、优化要点、验证结论与优化效果结论),必要时附关键日志片段或调用栈摘要。
- 涉及具体技术栈时,在当次对话中结合项目说明即可,不在本 SKILL 中写死具体标签或命令。