本篇是《Prometheus 从入门到生产实践》第 9 / 24 篇。配置以文中版本与环境前提为准,实验和故障均为教学场景。完整程序、配置及校验范围见配套实验包。

平均延迟很低,用户仍可能反映卡顿。因为平均值会把多数快速请求和少量慢请求混在一起。本篇学习直方图和分位数,也明确它们为什么是估计而不是每条请求的原始记录。
1. 经典 Histogram 记录了哪些序列
共享实验的 ops_demo_request_duration_seconds 使用秒为单位,经典桶边界为 0.1、0.3、1.0、+Inf。对每组业务标签通常会出现:
_bucket{le="..."}:不大于该上界的累计观察次数。_count:总观察次数。_sum:观察值之和,这里是累计耗时秒数。
桶是累计的,同一次 0.02 秒请求会被计入 0.1、0.3、1.0 和 +Inf 桶。把这些桶直接相加会重复计数。+Inf 桶应覆盖全部观察,通常与 count 对应。
若 B 包含 +Inf 在内的全部桶,一组业务标签会产生 B+2 条经典序列;乘上 route、method、code 和实例组合,成本就会增加。桶设计既影响统计精度,也影响资源开销。
2. 先计算窗口内平均值,检查单位
sum by (job) (
rate(ops_demo_request_duration_seconds_sum{job="demo"}[5m])
)
/
sum by (job) (
rate(ops_demo_request_duration_seconds_count{job="demo"}[5m])
)
分子是每秒累计耗时增加量,分母是每秒观察次数,两者相除得到平均秒数。没有观察时可能得到 NaN,不能强行读成零延迟。
请求耗时为非负值时,sum 是可按 counter 处理的累计量;如果用同样机制观察可正可负的业务值,就不能不加分析地套 rate(sum)。指标类型的业务含义很重要。
3. P95 应先合并桶,再计算
histogram_quantile(
0.95,
sum by (job, le) (
rate(ops_demo_request_duration_seconds_bucket{job="demo"}[5m])
)
)
先对各实例的桶求速率,再按 job、le 合并,最后计算分位数。经典直方图必须保留 le,因为函数要靠桶边界还原分布。
P95 大致表示所选观察总体中 95% 的值不超过这个位置,并不是“95% 请求恰好耗时这么多”。合并不同实例时,正确做法是合并可合并的分布,不是平均多个实例的 P95。分位数计算机制见 histogram_quantile 文档。
4. 桶宽决定你能知道多少
假设一个教学批次有 100 次观察:<=0.1 秒 40 次、<=0.3 秒 90 次、<=1 秒 98 次、全部 100 次。P95 落在 0.3~1 秒这一桶,经典线性插值约为 0.3 + (95-90)/(98-90) × 0.7 = 0.7375 秒。
这不代表真的存在一条 0.7375 秒请求。桶内实际分布没有保存,结果依赖插值假设。若业务最关心 300ms,就应该把 0.3 设计为明确边界,而不是在一个特别宽的桶里猜。
5. 有时阈值比例比 P95 更符合 SLO
当目标是“至少 99% 请求不超过 300ms”,可以直接统计边界桶比例:
sum by (job) (
rate(ops_demo_request_duration_seconds_bucket{job="demo",le="0.3"}[5m])
)
/
sum by (job) (
rate(ops_demo_request_duration_seconds_count{job="demo"}[5m])
)
它使用已经存在的 0.3 边界计数,无需为这个边界做桶内分位数插值。若要排除错误请求或某些路由,分子和分母应使用相同的业务范围。
实验正常请求约 0.02 秒,/api?slow=1 约 0.4 秒。两者应落入不同桶;操作系统调度等会造成实际耗时波动,所以以实际采集为准。
6. Summary 为什么不能随意跨实例聚合
Summary 可以在客户端计算特定窗口和目标分位数,并暴露带 quantile 的值。这些分位数不能简单求和或平均来得到全局 P95;不同实例的样本量也可能完全不同。
Summary 的 count、sum 在条件满足时仍可用于总体平均,但这不等于它的分位数也可合并。选择时要问:需要跨实例汇总吗、业务阈值会改吗、误差应该控制在哪个维度?比较见 Histograms and summaries 实践说明。
7. 原生 Histogram:相同目标,不同数据表示
原生直方图把一组分布保存在直方图样本中,不再要求每个桶都暴露成独立 le 序列。若客户端、抓取配置、Prometheus 版本和远端存储都支持,查询可类似:
histogram_quantile(
0.95,
sum by (job) (
rate(ops_demo_request_duration_seconds{job="demo"}[5m])
)
)
这是切换为原生格式后的示意,默认共享实验仍是经典桶,不能直接期待该查询返回原生样本。原生格式、启用选项、协议与客户端能力应逐段核对具体版本,不要把所有 Prometheus 3.x 都视为同一默认配置。
原生形式减少了经典桶序列的展开,但不表示标签基数或样本成本归零。不同 schema、混合样本与不兼容聚合可能产生注释或丢弃结果,查询 API 的 warnings、infos 也应纳入诊断。
8. 动手验证
分别产生正常与慢请求,观察 bucket 单调关系、+Inf 与 count,以及 300ms 比例。然后对比平均值、P95 和阈值比例,它们回答的不是同一个问题。
最后给每个延迟面板写上单位、窗口、观察范围和桶边界。没有这些说明,一个漂亮的 P95 数字也可能被误读成完全不同的承诺。
上一篇:Counter 与速率:rate、increase、重置和错误率 · 系列目录 · 下一篇:聚合与向量匹配:on、group_left、缺失和除零