2540 字
约 8 分钟
16
S27-A18 · 实测的问题定义:测什么,先写下来

有人花两天做了一次"缓存优化实测",最后写出的结论是"新方案比旧方案快"。没人能判断这句话有什么用——因为不知道"快"是指吞吐、延迟,还是 CPU 占用;也不知道"在什么条件下快"。

实测的最大浪费不是测错,而是测完之后发现"这个问题当初就没定义清楚"。这个成本差有个"复利"效果:十分钟的定义能省下两天,而那两天本来可以用于"第二次、第三次测量"。所以"先定义"不是为了严谨,而是为了让后面的时间花在能产出结论的地方。
而定义问题的成本只有十分钟,它决定了后面两天的活是否能产出可用的结论。


1. 问题定义的四个要素

要素 例子
比较对象 A 方案 vs B 方案
指标 P99 延迟、吞吐、成本
条件 数据量、并发、硬件
判定 快多少算"更好"

第四项最常缺:没有判定标准,"快 12%" 与 "快 3%" 都被写成"更快"第一项(比较对象)也会含糊:"新方案 vs 旧方案"里,"旧方案"是哪一版? 若旧方案本身刚改过,比较就失去基准,而这在快速迭代的团队里很常见。
,于是结论无法支撑决策。

第二项要具体到"哪个分位":P50 改善而 P99 变差是常见情形,只说"延迟"会掩盖它。

2. 一个好问题的三个特征

特征 反例
有明确的二选一 "评估一下缓存"
结论能改变一个决定 "了解一下性能"
失败也有信息量 "看看能不能更快"

第二项的判据可以写得更直接:"测完之后,我会因为哪个结果而改变什么"。答不出,就回到定义阶段,而不是开始跑。
第二项最实用:如果结论"无论好坏都不改变任何决定",那这次实测不该做。而这类"为了了解"的测速,往往占了一半以上的工作量。

第三项要求"预设判据":"如果 P99 没降到 80ms 以下,就不换"——这样"没达标"也是一个明确结论。

// 两种问题定义的对比
差:"评估新缓存方案的效果"
好:"在 3000 QPS、100 万行数据的条件下,
    新方案能否把 P99 从 120ms 降到 80ms 以下;
    若不能,维持旧方案"
// 差别:后者有对象、指标、条件、判定

问题定义写下来之后要"评审一次",成本两分钟,能省掉大部分"测完发现没回答":

// 两分钟自检(五项)
1 对象:A 与 B 具体是什么版本/配置
2 指标:主指标是哪个;约束是哪些
3 条件:数据/并发/硬件/参数/轮数
4 判定:什么结果算"更好"(数字)
5 会改变什么决定:若结论相反,我会做什么
// 任一项答不出 → 回到定义,不要开跑

3. 指标该怎么选

场景 主指标
交互式接口 P99 延迟
批处理 总耗时
服务容量 吞吐(QPS)
成本优化 单位成本
稳定性 错误率、超时率

第一项与第三项常冲突:吞吐高的方案可能 P99 更差。所以"主指标"要选一个,其余作为约束(如"P99 不退化超过 10%")。第二项(批处理)的"总耗时"要注意"分片":10 个任务并行跑完用 30 分钟,与串行跑完用 300 分钟,是两种完全不同的系统。所以"总耗时"必须配"并发度"一起写。

第五项最容易被忽略:"更快但错误率上升"不是优化。所以任何性能实测都应同时记录错误率。

4. 条件的写法

条件 必须写
数据规模 行数、平均大小
并发 客户端数、是否闭环
硬件 核数、磁盘类型
参数 非默认项
时长 每轮多久、跑几轮

第二项"闭环"很关键:闭环压测自带背压,它的吞吐上限不等于"能扛住的流量"。这一点不写清,结论会被误用。

第四项(非默认参数)最容易被省略,而它常是差异的真正来源:"新方案快 20%"可能是因为新方案用了更大的连接池,而这一点在参数块里没写。
第五项决定"结论是否稳定":单轮 10 秒的结果,噪声可能比差异还大。所以"跑 5 轮取中位数"应当写进方法。

// 条件块的标准写法(五行)
data:   1,000,000 rows, avg 420B, 3% 倾斜
load:   50 clients closed-loop, no think time
hw:     8 vCPU / 32GB / NVMe, 同机部署
params: pool=20, cache=on(其它默认)
runs:   5 轮 × 30s,取中位数与 P99

还有一个"最小可测"的原则:问题定义应当窄到"一次实验能回答"。如果它需要三次实验才能回答,那它其实是一组问题,应当拆开——否则每次实验都在"部分回答",而结论永远是"还要再看"。

// 一个过宽的问题 vs 拆开后的三个
宽:"新架构是否更好"
拆:① 单查询 P99 是否降到 80ms 以下
    ② 3000 QPS 下错误率是否 ≤0.1%
    ③ 单位成本是否下降 20% 以上
// 每个都能独立回答;三个都答"是"才换架构

5. 判定标准怎么定

类型 例子
阈值型 "P99 < 80ms"
相对型 "比现状快 20% 以上"
代价型 "快 20% 但成本不增"
排除型 "只要不退化就换"

第一项(阈值型)的"来源"要写清:"来自产品要求的 200ms 首屏"比"来自我们的经验"更可追责。因为阈值会被质疑,而"来源"是它的依据。
第一项最清晰,但需要"知道目标从哪来":通常来自 SLA 或用户体验阈值,而不是拍脑袋。

第三项在"优化"类项目里最实用:"更快但更贵"通常不是优化,而是换了一种代价。第四项(排除型)看着宽松,其实最实用:"只要不退化就换" 适合"新方案有其它好处(可维护性、生态)"的场景——此时性能不是主目标,而是"不能变差"的约束。

最后一项"事前记录"值得单独做:在开始测之前,把"我的预期"写下来。这一步的价值是"防止事后合理化"——人很容易在看到数据后解释成"本来就该这样",而写下的预期能让"意外"被看见。

// 事前预期(三行,写进记录顶部)
预期:新方案 P99 从 120 → 60ms,吞吐不变
理论依据:去掉了两次序列化
可能的意外:CPU 上升(因为压缩),或长尾变差(因为批量)
// 事后对照:命中/未命中都要写;未命中的部分才是新知识

6. 排错:五种"测完没用"

现象 成因 处理方向
结论无法支撑决策 无判定标准 先写判据
两个方案"各有优劣" 主指标没选 定一个主指标
复现不出来 条件没写 补条件块
结论被质疑 只有单轮 多轮取中位
测完发现问错问题 无问题定义 先花十分钟

第三项(复现不出来)有个"隐性成本":它会让结论被贴上"不可信"的标签,于是后续所有相关结论都要重新验证。而这本可以通过"条件块"避免。
第五项是最大浪费:两天的测量,回答了一个"没人在问"的问题。而它本来可以在十分钟的定义阶段被发现。

7. 边界:什么情况不必写问题定义

场景 为什么 做法
探索性测量("看看什么慢") 目的就是发现 先测再定
已知数量级差异 无需精确 粗测即可
只为自己判断 不被引用 随意
复现别人的已知结论 问题已被定义 照抄其定义

判断口径一句话:这个结论会不会被别人拿去支撑一个决定。最后一条与"文档"有关的边界:问题定义应当和结果写在同一份记录里,而不是分开。因为分开后,"结论"会被单独引用,而"条件与判定"留在另一份没人看的文档里。
会,就必须先写问题定义(对象、指标、条件、判定);只为自己看一眼,粗测就行。


8. 自测

  1. 问题定义的四个要素里,哪一个最常缺?没有它会产生什么后果?
  2. 一个好问题为什么要求"结论能改变一个决定"?"为了了解"的测速问题在哪?
  3. 为什么"吞吐高但 P99 更差"需要先定主指标?主指标与约束的关系是什么?
  4. 条件里"闭环/开环"为什么必须写?"跑几轮"影响什么?
  5. 四种判定标准里,哪一种在"优化"类项目里最实用?为什么"更快但更贵"通常不算优化?
  6. 判断"是否需要写问题定义"的那个问题是什么?
S27-A18 · 实测的问题定义:测什么,先写下来
https://www.clxhxhhr.top/posts/6529/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。