有尾巴的时候,平均值不描述任何一次真实的请求 —— 六成以上的请求比平均值快,而剩下那一小撮把平均拖了上去。 真正被人感受到的是尾巴。
这一页做三件别处很少做的事:给每个百分位配一个精确的区间, 在样本撑不起某个百分位时直接拒绝给数, 并且把「按请求算」换算成按页面算 —— 后者通常大一个数量级。
先看前提
平均 174 ms,中位 149 ms,而 p99 是 547 ms(95% 区间 524 – 570)。61% 的请求比平均值快 —— 有尾巴的时候平均值不描述任何一次真实的请求。
百分位,以及它们有多准
| 百分位 | 值 | 95% 区间(精确,不假设分布) |
|---|---|---|
| 平均 | 174 | 平均值不是百分位,也没有区间可言 —— 它只是被尾巴拉高了 |
| p50 中位 | 149 | 147 – 151 |
| p90 | 306 | 302 – 311 |
| p95 | 369 | 364 – 379 |
| p99 | 547 | 524 – 570 |
| p99.9 | 826 | 747 – 943 |
区间不是自助抽样,也不是正态近似:落在真实百分位以下的样本个数服从二项分布,不管耗时本身是什么分布,所以区间就是从二项分布两侧挑出来的两个真实观测。样本撑不起某一档时,这里给的不是一个含糊的数,而是不给 —— 那一档的区间会宽到没有意义。
百分位用的是「第 ⌈q·n⌉ 小」这个定义,所以每个数都是真实发生过的一次请求。插值出来的 p99 是一次从未发生的耗时,在长尾上它能落进一个一秒宽的空隙里。
按请求算,还是按页面算
被管理的是每个请求的百分位。被感受到的是每次打开页面有没有卡 —— 而一个页面要发很多个请求,只要有一个慢,这次打开就是慢的。
1−(1−p)k,p = 1.0%(超过 547 ms 的比例)。这条曲线是整页最该被记住的一行:把每请求的 1% 变成每页面的 18%,中间没有任何人做错什么,只是页面发了二十个请求。
时间花在哪
最后两个是好消息:要省下来的时间集中在一小撮请求上,而不是均匀摊在所有请求里。去看那一小撮,不要去优化中位数。
值得注意的
这里把「慢」定义成超过 p99(547 ms)。一个页面发 20 个请求,只要有一个慢,这次打开就是慢的:1−(1−p)^20 = 18%。被管理的那个数和被感受到的那个数差了 18.2 倍,而仪表盘上通常只有前一个。
它不知道的事
百分位不能平均。 各台机器 p99 的平均,既不是整体的 p99, 也不是别的任何东西 —— 它没有定义。同样地,把每分钟的 p99 再平均成一小时的 p99, 得到的也不是那一小时的 p99。要整体的百分位,只能把原始数据合起来重算一次。
百分位不能相加。 一次请求经过三个服务,每个 p99 都是 100ms, 端到端的 p99 不是 300ms —— 它取决于三段慢的时候是不是同时慢, 而这份数据里没有这个信息。
它只看你给的这一段。 一天里最糟的那十分钟, 摊进二十四小时的样本里几乎看不见。要看清楚,就分时段各算一次, 别指望一个整体百分位替你把它找出来。
贴进来的东西只用来算这一次,不写盘、不记日志、不存任何地方。 整个页面没有一行 JavaScript。