5.8 实验记录与常见反模式
上一节:5.7 噪声底线与回归检测 | 下一节:5.9 Lab 5 配套代码:05-experiment-design/08-records-and-antipatterns
一句话结论
实验记录的目的不是"存档",而是"让三个月后的你或另一个人能复现出同样的结论"。 而反模式是九种常见的自欺方式——它们的共同点是「让结论看起来比实际更有力」。
一、一份合格的实验记录包含什么
已在第 1 章和 docs/experiments/_TEMPLATE/ 里给出了完整模板。这里强调本章新增的四项:
| 新增项 | 为什么 |
|---|---|
| 轮数与每轮结果 | 单次结果不是结论(第 3 节) |
| CV 与噪声底线 | 判断差异是否可信的标尺(第 7 节) |
| 统计检验结果 | 差异显著性的证据(第 6 节) |
| 环境事件时间线 | 解释异常轮的依据(第 3 节) |
一个完整的「结果」章节应该长这样
## 结果
### 环境(12 轮实验期间)
- 噪声底线(本环境,P99):±8%(CV 3.1%,见 E05-noise 实验)
- 环境事件:T+15min 有一次日志轮转(已记录,未造成异常)
### 逐轮结果
| 轮次 | QPS | P50 | P95 | P99 | 错误率 | 备注 |
| --- | --- | --- | --- | --- | --- | --- |
| 1 | 498 | 32.1 | 68.4 | 93.2 | 0.00% | |
| 2 | 499 | 33.0 | 70.2 | 96.1 | 0.00% | |
| 3 | 497 | 32.4 | 67.9 | 94.0 | 0.00% | |
| 4 | 498 | 34.2 | 74.1 | 103.5 | 0.00% | ⚠️ 日志轮转期间 |
| 5 | 499 | 32.8 | 69.3 | 95.2 | 0.00% | |
### 汇总
- P99 中位数:95.2 ms(全部 5 轮)/ 94.6 ms(剔除第 4 轮)
- 四分位距:93.8 – 96.1 ms
- CV(轮间):3.1%
### 统计检验
- 与基线(E03 的 210 ms)对比:Mann-Whitney U,p = 0.008 → 显著
- 差异幅度:-55%(远大于噪声底线 8% 和 MDD 16%)
### 结论
优化有效。P99 从 210 ms 降到 95 ms,改善 55%,统计显著且远超噪声底线。
二、九种反模式
❶ Cherry-picking(挑好看的一次)
❌ 「我们测了 5 次,最好的那次 P99 是 88 ms,达标了。」
为什么错:这是选择性地报告数据,等于把噪声当成能力。
改法:报中位数,并列出所有轮次。
❷ 忽略预热
❌ 「服务启动后立刻压测,P99 = 800 ms,达标(SLO 是 1s)。」
为什么错:测的是冷启动,不是稳态(第 2 章 2.4 节)。
改法:独立预热阶段,数据不计入统计。
❸ 指标漂移(中途改了东西)
❌ 第 1 周用 A 脚本测,第 2 周用 B 脚本测,然后对比。
为什么错:两个数字不可比。
改法:脚本冻结快照。
❹ 单轮定论
❌ 「跑了一次,P99 = 95 ms。」
为什么错:无法区分差异与噪声。
改法:至少 3 轮,报中位数与波动范围。
❺ 把噪声当成果
❌ 「优化后 P99 从 210 ms 降到 198 ms,提升 5.7%。」
(而噪声底线是 ±8%)
为什么错:差异在噪声范围内。
改法:先测噪声底线,再判断。
❻ 不记录失败实验
❌ 只记录成功的优化,失败的实验"就不写了"。
为什么错:失败实验的信息量常常更大(「这条路走不通」),而且能防止重复踩坑。
改法:失败的实验也记录,状态标 abandoned,写明原因。
❼ 事后定标准
❌ 实验做完,看到 P99 降了 5%,于是把判定标准从"必须降 50%"改成"降 5% 就算成功"。
为什么错:这是给自己找理由。
改法:判定标准在实验前写进设计文档(第 1 节)。
❽ 用平均值汇报
❌ 「平均延迟 12 ms。」
为什么错:均值掩盖长尾(第 0 章 0.3 节)。
改法:报 P50/P95/P99 + 错误率。
❾ 混淆「统计显著」与「值得做」
❌ 「p = 0.03,统计显著,所以我们应该采纳这个优化。」
(而实际上 P99 只降了 5 ms,用户感知不到)
为什么错:统计显著是必要条件,不是充分条件(第 6 节)。
改法:同时判断「是否大于噪声底线」和「用户是否可感知」。
三、这些反模式的共同点
Cherry-picking (❶) ┐
单轮定论 (❹) ├─ 让结论看起来比数据支持的更有力
把噪声当成果 (❺) ┘
忽略预热 (❷) ┐
指标漂移 (❸) ├─ 让两组数据不可比,但看起来可比
事后定标准 (❼) ┘
不记录失败 (❻) ┐
用平均值 (❽) ├─ 隐藏了关键信息
混淆显著与值得 (❾) ┘
三类共同点:
- 夸大证据(❶❹❺)
- 伪造可比性(❷❸❼)
- 隐藏不利信息(❻❽❾)
最危险的是第二类:因为它不会让单个数字变好看,而是让「对比」本身失去意义——你以为在比较 A 和 B,实际上比较的是两个不同的实验。
四、一份「结论强度」自评表
报告任何性能结论前,过一遍这张表:
| 问题 | 如果答"否" |
|---|---|
| 实验前写了假设和判定标准吗? | 可能事后调整了标准(❼) |
| 只改了一个变量吗? | 无法归因(第 1 节) |
| 有对照组吗? | 只有一组数字,不构成结论 |
| 跑了几轮?≥ 3 吗? | 单轮定论(❹) |
| 报了中位数和波动范围吗? | 可能在 Cherry-picking(❶) |
| 知道环境的噪声底线吗? | 无法判断差异真假(❺) |
| 差异大于噪声底线和 MDD 吗? | 可能是噪声(❺) |
| 做了统计检验吗? | 缺少显著性证据(第 6 节) |
| 异常轮有成因解释吗? | 可能删掉了真实问题(第 3 节) |
| 观测配置前后一致吗? | 差异可能来自观测开销(第 4 节) |
| 有没有记录失败的实验? | 信息丢失(❻) |
| 结论里写了「未验证的场景」吗? | 过度承诺 |
十二条里有一条答"否",结论的强度就要打折扣。
五、本节小结
- 实验记录的四个本章新增项:轮数与每轮结果、CV 与噪声底线、统计检验结果、环境事件时间线。
- 九种反模式分为三类:夸大证据、伪造可比性、隐藏不利信息。
- 第二类(伪造可比性)最危险——它让"对比"本身失去意义。
- 用结论强度自评表(十二条)在报告前做一次检查。
- 失败实验必须记录——它们的信息量常常更大。
六、自测
- 一份报告写:「经过 5 轮测试,平均 P99 是 118 ms,最好的一轮是 88 ms,达标。」请指出其中至少三个反模式。
- 「p = 0.02,统计显著」和「P99 降了 5 ms,用户感知不到」这两句话可以同时成立吗?请解释,并说明你最终会怎么建议。
- 为什么「不记录失败实验」是一种反模式?请说出至少两个具体损失。
- 反模式:① 用平均值(❽)——118 ms 是被异常轮拉动的,可能掩盖「通常 90 ms,偶尔 300 ms」这个事实;② Cherry-picking(❶)——「最好的一轮是 88 ms」把噪声当成了能力,这一轮的 88 可能只是随机波动;③ 没有报波动范围(❹/❶)——读者无法判断稳定性;④ 潜在问题:没有说明噪声底线(❺)——无法判断 118 ms 与基线的差异是否真实;⑤ 还可能有 忽略预热(❷)或指标漂移(❸),取决于这 5 轮之间是否重启过服务、脚本是否变过。正确报告:逐轮列出 P99 + 中位数 + 四分位距 + CV + 噪声底线 + 与基线的统计检验结果。
- 可以同时成立。这是「统计显著」(差异不是偶然)与「实际有意义」(大到用户能感知、收益超过成本)的区别(第 6 节)。例子:P99 从 210 ms 降到 205 ms,5 轮数据非常稳定(CV 1%),所以 Mann-Whitney U 给出 p = 0.02——差异是真实的。但 5 ms 在 200 ms 级别上用户完全感知不到。最终建议:① 如果这个优化成本极低(比如改一行配置、无副作用),可以采纳,但不要为此写一篇"性能提升"的报告;② 如果有成本(引入缓存、增加复杂度、维护负担),建议不采纳,把时间投向差异更大的瓶颈;③ 在报告里明确写「统计显著但幅度小于用户感知阈值,收益有限」。关键原则:统计显著是必要条件,不是充分条件。
- 两个具体损失:① 重复踩坑——你(或同事)半年后遇到同样的问题,会再次尝试那条走不通的路,浪费同样的时间;而记录里如果写了「试过 X,无效,因为 Y」,就能直接跳过。② 丢失反例——失败实验里常有重要发现:「这个指标在某种负载下不降反升」「这个优化引入了新的瓶颈」「这个工具在当前环境下采不到数据」。这些都不是"失败",而是边界条件的发现。③ 还有第三个损失:无法评估"还有多少条路可走"——当你记录了 3 条失败路径和 1 条成功路径,你就知道探索空间的大致形状;反之永远觉得"可能还有更好的方案"。实践建议:在假设台账里维护「已排除」列表(第 0 章提到的三层记录体系),让失败的信息成为资产。