【背景】
做了 15 年数据工程,见过太多「上线一周后才发现 P99 涨了 30%」的事故,根因往往不是大重构,而是某次「顺手」的小改动:把索引顺序改了、把连接复用关了、把批处理粒度从 200 改成 500、把 synchronized 换成 ReentrantLock 顺手调了下粒度。功能测试全绿,性能测试根本没跑,等 SRE 报警回来已经晚了。
所以我一直想推动「性能回归进 CI」:每次 PR 都跑基准测试,超阈值直接 block。但推了两年,越推越觉得这件事没那么简单,今天想认真聊聊。
【常见的几种做法】 A 派(保守):性能是「大版本」才测的事,PR 级 CI 只跑功能测试。理由:基准测试太重(一次跑 10–30 分钟)、太不稳(CI 机器是共享的,邻居一跑就抖)、太玄学(P99 抖动 ±10% 是常态,门禁天天红)。 B 派(激进):性能回归必须进 CI,每次 PR 跑基准测试,超阈值直接 block,没有例外。理由:性能问题越晚发现越贵,PR 阶段改一行代码的成本,是线上救火的 1/100。 C 派(折中):只在核心链路(下单、支付、登录、列表查询)跑,且只盯 P99,不盯 P50 / 平均。理由:ROI 最大,噪声最小,开发同学也接受。 D 派(另一种折中):不进 CI 门禁,但进「发布前 checklist」,每次发版跑一次全量基准测试,结果归档,超阈值人工 review。理由:门禁太硬会逼着大家「绕过」,不如让性能数据说话、人来决策。
【我的倾向与理由】 我站 C 派,但有一个前置条件:基准测试的噪声必须先压到 ±3% 以内,否则 CI 会变成「红绿随机机」,门禁失去公信力,比没有门禁更糟。
具体做法(我们线上跑了 3 年的经验):
- 固定 CPU:CI 节点绑核(
taskset或 cgroup cpuset),禁止 CPU 频率调节(cpupower frequency-set -g performance); - 预热:至少 3 轮预热 + 10 轮正式,取正式轮的中位数(不是平均,避免长尾污染);
- 并发模型固定:用固定 QPS 压(不是固定并发线程),因为线程数在 CI 上不稳定;
- 阈值用「相对基线」而不是绝对值:比「上周同一 commit 的 P99」涨 10% 才告警,涨 20% 才 block。绝对值阈值会被业务迭代慢慢吃掉;
- 白名单机制:合理但会变慢的 PR(加日志、加校验、加限流)走「性能豁免」流程,reviewer 签字放行,豁免记录归档。
【想听大家在什么场景下怎么选】
- 你们线上怎么控制基准测试的噪声?有没有踩过「CI 机器邻居跑任务,P99 直接翻倍」的坑?
- 阈值怎么定:绝对值(P99 < 50ms)还是相对基线(比上周涨 10%)?你们团队有共识吗?
- 遇到「合理但变慢」的 PR(比如加了审计日志、加了参数校验、加了限流中间件),你们怎么放行?白名单怎么维护、谁签字?
- 你们 CI 跑基准测试用的是什么工具:JMH、wrk、vegeta、自研?有没有踩过工具本身的坑?
- 一个更尖锐的问题:如果性能门禁天天红、开发同学开始绕(拆 PR、关门禁、找 reviewer 签字),你的团队会怎么调整?
期待有真实线上数据的讨论,空谈「应该进 CI」的就不展开了。