文档目录

0.8 性能工程闭环

上一节:0.7 九条常见谬误自查清单 | 下一节:0.9 从 C++ 到 JVM:哪些直觉必须修正


一句话结论

性能工作不是一个「上线前压一次」的动作,而是一个八步循环。 每一次优化后的回归数据,都会成为下一轮的基线。


一、八步闭环

        ┌─────────────────────────────────────────────────┐
        │                                                 │
        ▼                                                 │
① 定义目标(SLO)                                           │
        ↓                                                 │
② 建立模型(Little's Law / 队列估算)                       │
        ↓                                                 │
③ 采集数据(压测 + 四层指标)                                │
        ↓                                                 │
④ 剖析归因(火焰图 / JFR / 线程快照)                         │
        ↓                                                 │
⑤ 定位根因(证据链 + 贡献占比)                              │
        ↓                                                 │
⑥ 优化(按优先级)                                          │
        ↓                                                 │
⑦ 回归验证(统计显著 + 长稳)                                │
        ↓                                                 │
⑧ 固化为门禁与监控 ─────────────────────────────────────────┘

为什么是循环而不是流程:第 ⑧ 步的监控数据会成为下一轮第 ① 步「目标是否需要调整」的输入;第 ⑦ 步的回归结果会成为下一轮第 ③ 步的基线。性能工作没有终点,只有「当前这一轮的稳定态」。


二、逐步拆解:每步做什么、产出什么、最容易在哪里失败

① 定义目标(SLO)

项 内容
做什么 把「快一点」翻译成「在 X 负载、Y 数据量下,Z 指标达到 W」
产出 SLO 表 + 延迟预算表
常见失败 目标没有负载前提(「P99 < 100 ms」在 1 QPS 和 10000 QPS 下是两个世界)
对应章节 第 1 章

② 建立模型

项 内容
做什么 用 Little’s Law 估算资源需求,用排队理论确定余量;在动手之前先有量级预期
产出 一张「预期容量 / 预期瓶颈」的假设清单
常见失败 跳过这一步直接压测,导致拿到一堆数字但不知道是否合理
对应章节 本章(0.4、0.5)

这一步最容易被跳过,但它的性价比极高:如果算出来「需要 60 个连接」而你的池只有 10 个,那你连压测都不用做就知道问题在哪。

③ 采集数据

项 内容
做什么 按设计的负载曲线压测,同时采集业务 / 延迟 / 资源 / 饱和度四层指标
产出 原始数据(曲线、火焰图、GC 日志、线程快照、DB 统计)
常见失败 只采客户端数据、闭环模型、不预热、数据量不真实、单次运行
对应章节 第 3、4、5 章

④ 剖析归因

项 内容
做什么 在拐点负载下采集 on-CPU / wall-clock / 分配 / 锁四类数据
产出 「时间花在哪条调用链上」的证据
常见失败 在低负载下剖析(什么都看不出来);只看 CPU 火焰图(漏掉等待类瓶颈)
对应章节 第 4、6 章

⑤ 定位根因

项 内容
做什么 把现象拆成可证伪的假设,逐个用命令验证,算出贡献占比
产出 按贡献排序的瓶颈清单 + 证据链 + 被排除的假设
常见失败 把相关性当因果(CPU 高 ≠ CPU 是原因,它可能是结果);没有贡献占比,无法排序
对应章节 第 6 章

⑥ 优化

项 内容
做什么 按优先级:不必要 → 算法/IO → 并发架构 → 缓存 → 常数 → JVM 参数
产出 每一项带「收益 / 代价」的变更
常见失败 顺序颠倒(先调 GC 参数去优化一个本该加索引的查询)
对应章节 第 7 章

⑦ 回归验证

项 内容
做什么 同环境、同脚本、≥3 轮对比;统计显著性判定;长稳(浸泡)验证
产出 带置信区间的收益报告
常见失败 只测性能不测长稳(优化引入缓慢泄漏);只给 after 不给 before
对应章节 第 5、7 章

⑧ 固化为门禁与监控

项 内容
做什么 微基准纳入 CI;生产配置 SLO 与饱和度告警;更新容量表
产出 自动化的防退化机制
常见失败 门禁阈值设在噪声以内 → 天天误报 → 被所有人忽略
对应章节 第 8 章

三、谁在哪个阶段介入

性能不是一个人的事。下表帮你判断什么时候需要拉谁进来:

阶段 你需要谁 为什么
① 定义目标 产品 / 业务 只有他们知道「多快算够用」「峰值是多少」
② 建立模型 你自己 这一步不需要别人
③ 采集数据 运维 / SRE 需要独立的压测环境与真实的机器规格
④ 剖析归因 你自己 + DBA 数据访问类瓶颈需要看执行计划和锁等待
⑤ 定位根因 你自己 这是最需要技术判断的一步
⑥ 优化 模块负责人 改别人的代码需要协作
⑦ 回归验证 你自己 + QA 功能正确性与性能要同时回归
⑧ 固化为门禁 平台 / CI 团队 流水线配置通常不在你的权限内

四、这个闭环在现实中怎么被打破

三种常见的「断环」方式,以及后果:

断在哪 表现 后果
跳过 ①②,直接 ③ 「先压一下看看」 拿到一堆数字,无法判断是好是坏,也不知道该改什么
跳过 ④⑤,直接从 ③ 到 ⑥ 「P99 高,加个缓存吧」 优化了非瓶颈,指标没变,还引入了新复杂度
跳过 ⑦⑧ 「优化完了,上线」 收益无法证明;三个月后同一个人再优化一遍同一个问题

注意中间那一条:「跳过剖析直接优化」是最高频的错误。它会让你在错误的地方努力,而且因为确实做了一些「看起来专业」的改动,团队会以为问题已经解决。


五、本节小结

  1. 八步闭环:定义目标 → 建立模型 → 采集数据 → 剖析归因 → 定位根因 → 优化 → 回归验证 → 固化为门禁。
  2. 它是循环:回归数据成为下一轮基线,监控数据提示目标是否需要调整。
  3. 最常被跳过的两步是 ② 建立模型(性价比最高)和 ④⑤ 剖析定位(决定优化是否有效)。
  4. 每一步都有明确的产出物;没有产出物的步骤等于没做。

六、自测

  1. 一个团队的做法是:上线前用 VU 模型压 10 分钟,看 QPS 达标就发布。请指出他们走完了闭环中的哪几步、跳过了哪几步,以及最可能的后果。
  2. 为什么「② 建立模型」被说成性价比最高的一步?请举一个它能帮你省下大量时间的具体例子。
  3. 如果你只能说服团队补上闭环中的一步,你会选哪一步?为什么?
  1. 走完了 ③(采集数据,但方式有缺陷:闭环模型、无预热说明、单次运行);可能部分做了 ①(如果有 QPS 目标)。跳过了 ①②的模型估算、④⑤的剖析定位、⑥的针对性优化、⑦的统计回归、⑧的门禁固化。后果:QPS 达标但不知道瓶颈在哪、不知道拐点和余量、生产环境一旦数据量增长或流量突增就会出问题,而且下次还得从头再来一遍。
  2. 因为它能在动手之前排除掉大量可能性。例子:先用 Little’s Law 算出「1000 QPS × P99 60 ms = 最坏情况下 60 个并发请求」,而你发现连接池只有 10——那么不用压测就知道瓶颈在连接池排队。省下的是搭环境、写脚本、跑测试、分析数据的几个小时甚至几天。
  3. 我会选 ④⑤ 剖析定位。理由:跳过①②只会让数据缺乏解释框架,跳过⑦⑧会让成果退化,但跳过④⑤会直接导致优化错地方——既浪费了工程时间,又因为「做了优化」而掩盖了真正的问题,是最贵的错误。