2989 字
约 9 分钟
14
S11-A15 · 样本的偏差:你看到的数不代表全部
2024-12-31
2026-11-09

抽取一周的慢查询日志,按耗时排序取前 200 条,逐条改成走索引、加缓存、拆大事务,三个月后业务侧反馈没变快。回头看才发现:那 200 条来自一个已经下线的老旧接口,而当前真正的耗时集中在另一批请求上——它们的单次耗时都没进"最慢榜",只是频次高、时间长,并且大量请求根本没被日志采到。

这不是分析不认真,而是样本从一开始就没代表全体。偏差诊断要回答的不是"数据准不准",而是"样本是怎么被选出来的,这个过程系统性地丢了谁"。


1. 抽样本身就会引入偏差

采样是最常见的优化,也是最隐蔽的偏差来源:被丢掉的往往正好是最需要看的那部分。

采样机制 表面的好处 系统性丢失 后果
按耗时阈值采(只记 >1s) 日志量可控 200–900ms 的长尾全部隐形 优化后仍"慢",因为改的是不适用的段
按比例随机采(如 1%) 无偏、成本低 极低频但影响大的错误 单次致命的异常几乎不被采到
队列满就丢 采集不阻塞业务 高负载时段的样本被优先丢 越是故障时刻越没数据
采样后聚合 存储便宜 分布形状 平均值掩盖双峰与尾部

关键判据:任何"满了就丢""超时就截断"的采集,都在丢掉最慢的那部分——慢请求占用连接时间长,更容易撞上队列满或超时截断。与之相对的偏差是"只记录慢的":若日志只留超过阈值的请求,任何基于它算出的"平均耗时"都偏高,用它论证"我们整体变快了"是循环论证。

logging:
  trace_sample_ratio: 0.01        ─── 1% 随机采样,尾部错误大概率漏
  slow_threshold_ms: 1000         ─── 低于阈值的请求不进日志
  queue:
    size: 8192
    on_full: drop_newest          ─── 高负载时丢的正是当时的新请求
  max_body_bytes: 4096            ─── 大请求被截断,参数不可见

把 on_full 改成 drop_oldest 并不解决问题——只是把偏差从"丢最新"换成"丢最早"。真正的修法是把采样决策与判定结论分开:采样可以偏,结论不能靠采样单独支撑。


2. 四类偏差与分母的选择

偏差 机制 典型案例 识别信号
幸存者偏差 只统计活下来的对象 只看当前在用的接口,已下线/报错的不见 分母里缺少失败与退出项
时长偏差 观察窗与现象时长不匹配 慢查询"排队"是队列深度问题,采样只看到执行时长 指标随时间流逝而系统性变化
采集偏差 采集点位置带来选择性 只看成功请求算响应时间,失败与重试被排除 成功率越高,性能数字越漂亮
反馈偏差 样本由用户主动产生 只看用户报障的慢,不看沉默流失的那批 结论只覆盖投诉渠道

其中"只看成功请求"最容易被忽略:它把超时、被拒、重试后成功全部排除,于是系统越不稳定,测出来的性能越好。

正确分母:全部请求 = 成功 + 失败 + 超时 + 重试次数(重试要重复计入耗时)
错误分母:仅最终成功的请求
─── 稳定性越差,两分母差异越大,方向恒为"高估性能"
─── 修法:分母取自网关计数,而非应用侧成功日志

四类偏差里,只有采集偏差是"改采集点就能消除"的;幸存者与时长偏差需要在设计分析时就把对象与窗口定义清楚;反馈偏差无法消除,只能靠主动抽样补齐沉默人群。


3. 聚合掩盖分布,自选样本掩盖人群

监控平台默认给平均值,而平均值是最没有信息量的分布统计量之一。

统计量 能看到 看不到 什么时候必须换
平均值 总体水平 双峰、长尾、少数极慢 任何时候要看"最差体验"
p95 / p99 尾部状况 极少数灾难性样本 尾延迟敏感的业务
最大值 极端个例 是否普遍 定位单次事故
直方图/分布 形状 精确定义 判断"是两个群体还是一个"
// 只有平均值时,"变慢"可能只是流量结构变了
avg(rate(http_request_duration_seconds_sum[5m]))
  / avg(rate(http_request_duration_seconds_count[5m]))

// 看分布才分得出是整体右移还是尾部变长
histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
histogram_quantile(0.50, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))

自选样本的落差同样明显:用户主动反馈、内测用户、内部员工,这三类人群的设备和网络条件都与全体不同。

样本来源 与全体的落差
报障用户 网络差、机型旧、使用频繁,慢的比例被放大
内测用户 新版本、高性能设备,问题被掩盖
内部账号 数据规模小、权限高,永远不触发最慢路径

覆盖率必须从两头记账,否则永远不知道丢了多少:

$ curl -s localhost:9090/metrics | grep -E 'requests_total|sampled_total'
app_requests_total{app="web"} 4821930    ─── 业务侧真实请求数
app_sampled_total{app="web"}   48219     ─── 采集侧落盘条数
app_sampled_dropped_total{app="web",reason="queue_full"} 12044
$ python3 - <<'P'
cov = 48219 / 4821930
print(f"覆盖率 {cov:.2%}, 丢弃占比 {12044/48219:.1%}")
P

结论必须随样本说明一起交付,否则下一个读者会把带偏差的数当成全量事实:

| 项 | 内容 |
|---|---|
| 样本窗口 | 2026-03-01 ~ 03-07(含 2 天周末) |
| 覆盖率 | 1% 随机采样,另含全部 >1s 请求 |
| 已知缺口 | 超时请求未落盘;重试未合并 |
| 可外推到 | 工作日白天、大请求为主的服务 |

4. 对比实验、辛普森悖论与"多看几天"

当两个版本、两个集群、两个时间段各看总体时结论一致、分开看却相反,就是分层问题。

分组 新版本 p99 旧版本 p99 样本结构
小请求 12ms 15ms 新版本占比 20%
大请求 800ms 700ms 旧版本占比 80%
合计 170ms 152ms 新版本总体更差,但每一层都更好

单独看合计会得出"新版本更慢",实际上它每一层都更快,只是请求结构被大请求拉偏了。分层对比比总体对比更接近真相,前提是分层维度选得对——分层维度应该是"会影响结果的那个变量",而不是随手切的维度。

-- 总体:结论可能被结构差带偏
SELECT version, quantile(0.99)(duration_ms) AS p99
FROM request_log WHERE ts >= '2026-03-01' GROUP BY version;

-- 分层:按请求体量与缓存命中情况切开再比
SELECT version, body_bucket, cache_hit,
       count(*) AS n, quantile(0.99)(duration_ms) AS p99
FROM request_log WHERE ts >= '2026-03-01'
GROUP BY version, body_bucket, cache_hit
ORDER BY body_bucket, cache_hit, version;

判断顺序建议固定为:先看样本覆盖率 → 再看分布形状 → 最后看总体均值。先看均值会让人过早形成结论,之后所有动作都在为这个结论找证据。

"数据不够就多看几天"有明确适用范围:在季节性存在时完全无效。周末流量与工作日不是同一分布,多看几天只是把两类人群混在一起。

场景 看更久是否有效 替代做法
日周期明显(工作日/周末) 无效 同比或按天分层
有明确事件影响(活动、发版) 无效 剔除事件窗或单独建模
平稳期的小幅波动 有效,降方差 仍应看分布而非均值
低频指标(月度任务失败) 有效但代价高 优先改采集而非等

5. 排错:五种"数看着对但结论错"

现象 偏差类型 第一步动作
优化后指标没变,用户仍说慢 采集偏差 + 尾部被采样采掉 核对分母是否含失败与重试
平均延迟平稳,个别用户投诉严重 聚合掩盖分布 拉 p99 与直方图对比
内测评价很好,上线后大量抱怨 自选样本 对比内测与全体的人群结构
A/B 总体更差但各分层更好 辛普森悖论 检查两组的结构占比
加了几天数据后结论翻转 季节性混入 做同比,或按周期分层

6. 修正动作、度量与边界

发现偏差后动作的选择依据只有一条:偏差能否事后重建。

偏差可事后重建?
├─ 能,且只影响可标识子集 → 加权修正,写明权重来源与不确定性
├─ 能,但影响全体         → 重跑分析(原始数据仍在,成本低于重采)
└─ 不能(已被采样丢弃)   → 修采集向前看,历史标注断点,不伪装成可比
度量 含义 观测方式
样本覆盖率 被采集的请求占全部的比 采集侧计数与业务侧计数比对
分布形状 单峰还是多峰 直方图目视 + 分位数间距
分层方差 各分层之间的差异程度 分层后组间统计量差异
结论翻转次数 修正样本后结论改变次数 复盘记录计数
边界场景 结论
样本量已足够大且分布平稳 偏差风险低,可直接用总体统计量
决策本身鲁棒(改不改都不影响动作) 不值得为精度投入
采集成本极低(全量可存) 不做抽样,偏差问题自然消失
一次性探索性分析 先出量级结论,标注样本限制即可

一句话判据:会重复使用、会被引用的结论必须做偏差诊断;一次性且不影响后续动作的判断不必。前者错一次会被复制很多遍,后者错一次代价有限。


自测

  1. 为什么"队列满就丢"这种采样方式,丢掉的往往正是最该看的请求?
  2. "只看成功请求"计算响应时间,会在系统不稳定时把结论推向哪个方向?
  3. 平均延迟平稳但投诉集中在少数用户,你会先取哪个统计量,为什么?
  4. 辛普森悖论的场景里,分层维度应该按什么标准选?
  5. 什么情况下"多观察几天"对消除偏差完全没有帮助?
  6. 什么样的偏差可以事后修正,什么样的只能改采集并标注断点?
S11-A15 · 样本的偏差:你看到的数不代表全部
https://www.clxhxhhr.top/posts/6325/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。