多实例接口的 p95 怎么算?先合并 Prometheus 直方图的桶
服务级 p95 应从服务级请求分布计算。使用 Prometheus 经典直方图时,对每条累积桶计数先取 rate(),按服务与 le 聚合,再交给 histogram_quantile()。实例 p95 的平均值会把低流量实例的尾延迟当成和主实例同等重要,即使加权平均也无法从各实例分位数恢复总体分位数。
1. 用 1000 个合成请求验证差异
保存为 histogram_p95_lab.py 并运行。假设每组桶计数来自同一个固定窗口。样本从零起算,含三个有限桶和 +Inf 桶,数值单位为秒。函数只实现目标分位数落在有限桶内的线性插值,以复算文档中的计算规则。
from math import inf, isclose
bounds = (0.1, 0.3, 1.0, inf)
fast = (0, 980, 990, 990)
slow = (0, 0, 10, 10)
def estimate_p95(cumulative):
rank = 0.95 * cumulative[-1]
lower_bound, previous = 0.0, 0
for upper_bound, count in zip(bounds, cumulative):
if count >= rank:
if upper_bound == inf or count == previous:
raise ValueError("sample requires a populated finite bucket")
return lower_bound + (upper_bound - lower_bound) * (
(rank - previous) / (count - previous)
)
lower_bound, previous = upper_bound, count
raise ValueError("invalid cumulative counts")
merged = tuple(a + b for a, b in zip(fast, slow))
fast_p95, slow_p95 = estimate_p95(fast), estimate_p95(slow)
combined_p95 = estimate_p95(merged)
print("merged buckets:", merged)
print(f"instance p95s: {fast_p95:.4f}s, {slow_p95:.4f}s")
print(f"mean of instance p95s: {(fast_p95 + slow_p95) / 2:.4f}s")
print(f"merged p95 estimate: {combined_p95:.4f}s")
print(f"at or below 300ms: {merged[1] / merged[-1]:.1%}")
assert isclose(combined_p95, 0.1 + 0.2 * 950 / 980)
assert merged[1] / merged[-1] == 0.98
merged buckets: (0, 980, 1000, 1000)
instance p95s: 0.2919s, 0.9650s
mean of instance p95s: 0.6285s
merged p95 estimate: 0.2939s
at or below 300ms: 98.0%
2. 从计算对应到 PromQL
假设指标单位为秒,所有实例的桶边界一致,且 service 标签标识服务。经典直方图查询如下。
histogram_quantile(
0.95,
sum by (service, le) (rate(http_request_duration_seconds_bucket[5m]))
)
rate() 放在 sum 内,先让 Prometheus 按原始序列处理计数器重置。查询还须保留 le,供分位数函数识别桶的上界。若希望按路由观察,在埋点提供稳定的路由模板标签,并把它加入聚合维度。
判断“有多少请求在 300 ms 内完成”时,若所有实例都设有该边界,可以直接用该桶的速率除以总请求速率。例如 le 的实际标签值为 0.3 时可用下式。无请求时的比率缺乏分母,告警逻辑应显式处理零流量窗口。
sum by (service) (
rate(http_request_duration_seconds_bucket{le="0.3"}[5m])
)
/
sum by (service) (rate(http_request_duration_seconds_count[5m]))
合并结果的 300 ms 达标率来自累积桶计数。p95 落在 100–300 ms 桶时,需要假设桶内分布并插值,估算精度取决于桶边界。变更桶边界期间,需确认参与同一聚合的实例是否仍使用一致的分布定义。
小结
服务级分位数先按实例合并桶。固定阈值的达标率在对应桶存在时,可直接从桶和总数计算。
数据来源与检索时间
| 来源 | URL | 检索日期 | 对应内容 |
|---|---|---|---|
| Prometheus histogram_quantile | https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile | 2026-11-13 | 经典桶聚合、le 和线性插值 |
| Prometheus rate | https://prometheus.io/docs/prometheus/latest/querying/functions/#rate | 2026-11-13 | 处理计数器重置和先 rate 后聚合 |
| Prometheus 直方图与摘要 | https://prometheus.io/docs/practices/histograms/ | 2026-11-13 | 分位数聚合与固定阈值达标率 |
核验说明
- Windows、Python 3.11.9 对合成桶计数复算并验证关键结果。两条 PromQL 按 Prometheus 官方文档核对,未在 Prometheus 服务实例执行。