这个函数做什么
histogram_quantile 依据直方图的桶计数估算分位数:它接收一个分位参数与桶序列,返回估算出的取值阈值。由于桶是累计计数,函数能在查询时按需算出 P95、P99,也能把多个实例的桶合并后再计算,这是直方图相对客户端摘要的核心优势。
为什么必须先聚合桶
直接对每个实例算分位,会得到一堆互不相干的曲线,也无法回答整体是多少。正确做法是先按 le 聚合桶,把同一维度上各实例的桶计数相加,再交给函数计算;若还要按服务或接口分别看,把对应标签加进分组即可。
分桶设计决定精度
分位是估计值:样本落在两个边界之间时只能按线性插值推算,桶越稀疏误差越大。桶边界应覆盖业务关注区间,在阈值附近加密;若大量样本落进最高桶,函数会返回一个偏低的结果,看板上反而显得正常。
基本信息
| 函数签名 | histogram_quantile(分位, 桶向量) |
|---|---|
| 输入要求 | 带 le 标签的累计桶序列 |
| 聚合要点 | 先 sum by (le) 合并桶,再计算分位 |
| 取值含义 | 0.95 表示 P95,输出单位与桶一致 |
| 常见坑 | 末桶为无穷大时返回值属外推估计 |
典型使用场景
- 接口延迟看板:按接口计算 P95 与 P99,替代没有信息量的平均值
- SLO 度量:延迟类目标通常以分位或超阈值比例表达
- 容量评估:分位曲线抬升说明尾部能力不足,比平均值更早预警
- 多实例聚合:先合并桶再算分位,得到整个服务的真实分位
- 告警表达式:分位与阈值比较,比用平均耗时要贴近用户感受
正确与错误写法对比
正确与错误写法对比
# 正确:先聚合桶,再算整体 P95
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))
# 正确:按服务分别计算
histogram_quantile(0.95, sum by (service, le) (rate(http_request_duration_seconds_bucket[5m])))
# 错误:对实例级分位再做平均,数学上不成立
# avg(histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])))分桶与精度的关系
| 分桶情况 | 对分位的影响 | 处理方式 |
|---|---|---|
| 桶过少 | 分位呈阶梯跳跃 | 按业务区间增加边界 |
| 边界远离关注点 | 关键阈值附近误差大 | 在阈值附近加密桶 |
| 大量样本落入最高桶 | 分位被明显低估 | 整体上移桶上界 |
| 边界过于密集 | 序列数与存储开销上升 | 只保留真正关心的档位 |
常见错误与结果
| 写法 | 结果 | 原因 |
|---|---|---|
| 漏掉按 le 聚合 | 出现多条互不相干的曲线 | 没有跨实例合并桶 |
| 对分位求平均 | 整体分位明显偏低 | 分位不能线性平均 |
| 对总和做分位计算 | 结果无意义 | 函数只接受桶序列 |
在客户端定义桶边界
在客户端定义桶边界
# 以 Go 客户端为例,自定义耗时桶(单位:秒)
buckets = [0.05, 0.1, 0.3, 0.5, 1, 3, 5] # 覆盖常见接口耗时区间
# 桶边界一旦发布就很难更改:改动等于改了统计口径
# 新增边界会让新旧数据不可直接比较,需在看板与告警上同步调整与分位搭配的指标
- 平均耗时:整体抬升还是仅尾部变差,需要两条一起看
- 超阈值比例:用桶相减得到超过阈值的请求占比,适合直接做告警
- 请求总数:作为分母与流量基数参考
- 成功与失败分开:快速失败的请求会拉低耗时,需分组统计
分位与平均值配合使用:平均值明显上升说明整体变慢,分位抬升而均值平稳说明只是尾部问题。
改动桶边界相当于改变统计口径:看板、告警与历史对比都要一起评审,否则同一张图前后不可比。
先确认指标上是否真有桶序列:只有直方图类型才会生成带 le 标签的桶,普通计数或摘要类指标上套用本函数只会得到空结果。