第4章:基础统计量——均值、标准差、百分位数、滚动窗口
说实话,很多人在设定 Spot 过滤阈值时,一上来就拍脑袋。比如觉得「超过 100 就是异常」,或者「连续 3 次就算报警」。这种直觉在数据平稳时或许管用,但一旦业务量波动、数据分布变了,阈值就成了摆设。
所以,这一章我们来聊聊基础统计量。我个人习惯,每次接手一个新的 Spot 系统,第一件事就是把这四个量算一遍。它们就像体检报告里的几项核心指标,能让你对数据有个整体认知。
4.1 均值:最直观的「中心」
均值就是平均数,所有值的总和除以个数。它告诉你数据大致聚集在哪个位置。
在阈值设定中,均值通常作为基准线。比如我设置一个慢查询检测阈值——均值是 50ms,那我可以先试着把警戒线设在均值 + 某个偏移上。但这有个前提:你的数据不能有太多极端值。否则一个 1000ms 的请求就能把均值拉到很高,导致阈值失效。
看个简单计算:
假设有 5 次请求延迟:30, 45, 50, 55, 320 (ms)
均值 = (30 + 45 + 50 + 55 + 320) / 5 = 100 ms
你看,明明大部分请求在 50ms 左右,但一个 320ms 就把均值抬高到了一百。这种情况下,均值做基准就有点勉强了。我一般会先看看数据分布,如果发现极端值较多,会结合中位数或百分位数来判断。
4.2 标准差:数据的「离散程度」
均值告诉你「中心在哪」,标准差则告诉你「数据有多散」。说白了,它衡量每个数据点与均值的平均偏差。
标准差大,说明数据忽高忽低,波动剧烈;标准差小,说明数据稳定,大家差不多。这个信息对阈值设定特别关键——
核心思路:阈值通常设为均值 ± k × 标准差。k 一般取 2 或 3。如果数据近似服从正态分布,均值 ± 2σ 覆盖了约 95% 的数据,± 3σ 覆盖了约 99.7%。
但是,我特别想提醒一点:不要迷信正态分布。我在实际项目中遇到过很多次,数据分布根本不是钟形曲线,而是长尾、双峰甚至多峰。这时候你用「均值 ± 3σ」去卡阈值,漏报或误报率会非常高。
举个例子:
业务 A:延迟数据集中在 10-20ms,偶尔跳到 200ms
业务 B:延迟数据均匀分布在 30-100ms 之间
同样的均值 50ms,但标准差完全不同。
A 的标准差很小,200ms 明显异常;
B 的标准差很大,200ms 可能只是正常波动。
这就是为什么,只看均值不看标准差,阈值设定就是盲人摸象。
4.3 百分位数:更稳健的「边界」
百分位数解决了均值的两个痛点:对极端值敏感、无法描述数据分布形状。
我们常用的是 P50(中位数)、P95、P99。P99 的意思是,99% 的数据都小于等于这个值。换句话说,超出 P99 的点,你可以认为是“较极端”的。
我个人比较喜欢用 P99 作为报警触发线。为什么?因为它在绝大多数情况下能避免被突发毛刺干扰。
| 百分位 | 含义 | 阈值设定参考 |
|---|---|---|
| P50(中位数) | 一半数据低于此值 | 直观的中心,不受极端值影响 |
| P95 | 95% 数据低于此值 | 能容忍少数异常(如流量突增) |
| P99 | 99% 数据低于此值 | 严格,适合对延迟/错误率要求高的场景 |
| P99.9 | 99.9% 数据低于此值 | 极严格,用于关键路径监控 |
计算百分位数需要对数据进行排序。比如有 100 个点,P95 就是第 95 个排好序的值。实际中我们不会每次实时算全量,而是用近似算法(比如 T-Digest、GK 算法)来做。
我之前维护过一个高并发网关,延迟 P99 是 120ms,但 P50 才 20ms。这说明大部分请求很快,但有一小撮非常慢。这种情况下,用 P99 做阈值比用均值靠谱多了——因为均值会被中段请求填平,看不出尾部的真实情况。
4.4 滚动窗口统计:让阈值「动」起来
前面说的均值、标准差、百分位数,都是基于一段「固定」的历史数据算的。但现实中的数据,业务量是有波动的——
- 白天峰值 vs 夜间低峰,数据特征完全不同
- 大促期间 vs 平时,阈值也要自适应
- 系统更新后,性能可能变化,旧阈值不再适用
这时就要用到滚动窗口。只取最近 N 条数据或最近 T 秒的数据来计算统计量,让阈值随着时间推移「自我更新」。
常见的窗口类型:
- 滑动窗口(Sliding Window):固定长度,每次新增一条就移除最旧的一条
- 时间窗口(Time Window):固定时间段,比如近 5 分钟内的数据
- 指数加权移动平均(EWMA):给较新的数据更高权重,响应更快
我自己常用的策略是「双窗口」:短期窗口(比如 5 分钟)检测快速变化,长期窗口(比如 1 小时)给出参考基线。两者一对比,就能判断是否真的有异常,还是自然波动。
一个小经验:滚动窗口的长度不要设置得太短,否则阈值会跟着噪声剧烈抖动。太长了又反应迟钝。我开始做的时候吃过这个亏——窗口设成 30 秒,结果每秒都在变动报警阈值,根本无法决策。后来改成 5 分钟窗口,配合 1 小时基线,效果就好多了。
4.5 综合运用:一个实际的案例
我来分享一个之前处理过的真实场景。
一个电商平台的双十一流量监控,我们需要对「单位时间请求量」做预警,防止系统容量被打满导致雪崩。
最初,我们设死阈值 2000 QPS。结果平时没问题,一到白天峰值就被误报;到了夜间,真正的爬虫攻击流量才 1000 QPS,阈值太高又漏报了。说白了,固定阈值根本管不住动态场景。
后来调整为滚动窗口 + 百分位数方案:
窗口长度:10 分钟
统计量:计算窗口内请求量的均值、标准差和 P99
规则:
- 若当前 QPS > 均值 + 3 × 标准差,且持续 30 秒 → 触发告警
- 若当前 QPS > P99,且持续 10 秒 → 触发告警(更灵敏)
- 每分钟重算一次窗口
效果立竿见影。白天峰值时阈值自动升高到 3500 QPS,误报率降到零;夜间爬虫攻击流量虽然只有 1200 QPS,但因为远高于夜间均值(400 QPS),触发了「均值 + 3σ」规则,成功捕捉了异常。
当然,这套方案也不是一劳永逸的。我后来发现,如果业务突然出现周期性爆发(比如整点秒杀),滚动窗口刚更新完阈值就被刷上去,导致下一个秒杀点触发不了告警。所以又加入了「滑动基线对比」——不仅看短期窗口,还对比过去 24 小时同时间段的参考值。
注意:滚动窗口统计对计算资源和存储有要求。如果数据量非常大(每秒百万级),不要实时全量重算。可以用近似数据结构(如 HLL、T-Digest)来节省性能。
4.6 小结
这一章,我们聊了均值、标准差、百分位数和滚动窗口统计。它们不是孤立的,而是一套从「静态描述」到「动态适应」的完整体系。
我把它们组合成一个简单的工作流,供你参考:
- 先用百分位数了解数据分布,排除极端值干扰
- 用均值 + 标准差建立初始阈值框架
- 再通过滚动窗口让阈值自适应
- 最后,根据实际报警效果微调窗口长度和百分位选择
嗯,这样一套下来,阈值设定就不再是拍脑袋,而是有据可依了。