关于监控:prometheus-两种分位值histogram和summary对比histogram线性插值法原理说明

前言

  • prometheus官网文档中对于两种类型的比照阐明
  • 上面我总结一些比照点
比照点 histogram summary
查问表达式比照 histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) http_request_duration_seconds_summary{quantile="0.95"}
所需配置 抉择适合的buckets 抉择所需的φ分位数和滑动窗口。其余φ分位数和滑动窗口当前无奈计算。
客户端性能开销 开销低,因为它们只须要减少计数器 开销高,因为流式分位数计算
服务端性能开销 开销高,因为须要在服务端实时计算(而且bucket值指标基数高) 开销低,能够看做是gauge指标上传,仅查问即可
分位值误差 随bucket精度变大而变大(线性插值法计算问题) 误差在φ维度上受可配置值限度
是否反对聚合 反对 不反对(配置sum avg等意义不大)
是否提供全局分位值 反对(依据promql匹配维度决定) 不反对(因为数据在每个实例/pod/agent侧曾经算好,无奈聚合)

histogram 线性插值法

histogram_quantile为何须要先算rate

  • 因为每个bucket都是counter型的,如果不算rate那么分位值的后果曲线是一条直线
  • 原理是因为counter型累加,不算rate并不知道以后bucket的增长状况,换句话说不晓得这些bucket是多久积攒到当初这个值的

什么是线性插值法

  • 之前浏览很多文章都提到histogram采纳线性插值法计算分位值会导致肯定的误差
  • 对这个线性插值法总是了解的不到位
  • 在查看完代码之后明确了

    代码剖析

  • 代码地位:D:workgo_workpkgmodgithub.comprometheusprometheus@v0.0.0-20201209205804-66f47e116e00promqlquantile.go

bucket数据结构

  • 其中bucket 代表当时定义好的bucket
  • upperBound代表这个bucket的上限值
  • count 代表这个小于等于这个upperBound的个数/次数
  • workqueue_work_duration_seconds_bucket{name="crd_openapi_controller",le="10"} 65246
  • 所以上述表达式含意为 workqueue_work_duration_seconds小于10秒的有65246
type bucket struct {
 upperBound float64 count      float64}
type buckets []bucket
func (b buckets) Len() int           { return len(b) }
func (b buckets) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound }

外围计算函数

  • 外围函数如下
func bucketQuantile(q float64, buckets buckets) float64 {
 if q < 0 { return math.Inf(-1) } if q > 1 { return math.Inf(+1) } sort.Sort(buckets) if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) { return math.NaN() }
 buckets = coalesceBuckets(buckets) ensureMonotonic(buckets)
 if len(buckets) < 2 { return math.NaN() } observations := buckets[len(buckets)-1].count if observations == 0 { return math.NaN() } rank := q * observations b := sort.Search(len(buckets)-1, func(i int) bool { return buckets[i].count >= rank })
 if b == len(buckets)-1 { return buckets[len(buckets)-2].upperBound } if b == 0 && buckets[0].upperBound <= 0 { return buckets[0].upperBound } var ( bucketStart float64 bucketEnd   = buckets[b].upperBound count       = buckets[b].count ) if b > 0 { bucketStart = buckets[b-1].upperBound count -= buckets[b-1].count rank -= buckets[b-1].count } sql:=fmt.Sprintf("%v+(%v-%v)*(%v/%v)", bucketStart, bucketEnd, bucketStart, rank, count,
 ) log.Println(sql) return bucketStart + (bucketEnd-bucketStart)*(rank/count)}
  • 咱们当初有这些数据,而后求75分位值
a := []bucket{
 {upperBound: 0.05, count: 199881}, {upperBound: 0.1, count: 212210}, {upperBound: 0.2, count: 215395}, {upperBound: 0.4, count: 319435}, {upperBound: 0.8, count: 419576}, {upperBound: 1.6, count: 469593}, {upperBound: math.Inf(1), count: 519593},}
q75 := bucketQuantile(0.75, a)
  • 其计算逻辑为:依据记录总数和分位值求指标落在第几个bucket段b
  • 依据b失去起始bucket大小bucketStart,终止bucket大小bucketStart ,本bucket宽度 ,本bucket记录数
  • 依据本段记录数和分位值算出指标分位数在本bucket排行rank
  • 最终的计算形式为分位值=起始bucket大小+(本bucket宽度)*(指标分位数在本bucket排行/本bucket记录数)
  • 换老本例中: q75=0.4+(0.8-0.4)*(70259.75/100141) = 0.6806432929569308
2021/02/02 19:08:55 记录总数 = 5195932021/02/02 19:08:55 指标落在第几个bucket段= 4
2021/02/02 19:08:55 起始bucket大小= 0.4
2021/02/02 19:08:55 终止bucket大小= 0.8
2021/02/02 19:08:55 本bucket宽度= 0.4
2021/02/02 19:08:55 本bucket记录数= 100141
2021/02/02 19:08:55 指标分位数在本bucket排行= 70259.75
2021/02/02 19:08:55 分位值=起始bucket大小+(本bucket宽度)*(指标分位数在本bucket排行/本bucket记录数)
2021/02/02 19:08:55 0.4+(0.8-0.4)*(70259.75/100141) = 0.6806432929569308

那线性插值法的含意体现在哪里呢

  • 就是这里 本bucket宽度*(指标分位数在本bucket排行/本bucket记录数)
  • 有个假设:样本数据这个指标bucket中依照均匀距离均匀分布
  • 举例 100141个样本在0.4-0.8 bucket中均匀分布
  • 如果实在值散布凑近0.4一些,则计算出的值偏大
  • 如果实在值散布凑近0.8一些,则计算出的值偏小
  • 这就是线性插值法的含意

histogram 高基数问题

  • 具体能够看我之前写的文章prometheus高基数问题和其解决方案

危害在哪里

  • 一个高基数的查问会把存储打挂
  • 一个50w基数查问1小时数据内存大略的耗费为1G,再叠加cpu等耗费

为何会呈现

  • label乘积太多 ,比方bucket有50种,再叠加4个10种的业务标签,所以总基数为 50*10*10*10*10=50w

summary 流式聚合

一个qps为1万的http服务接口的分位值如何计算

  • 假如以1秒为窗口拿到1万个申请的响应工夫,排序算分位值即可
  • 然而1秒窗口期内,快的申请会多,慢的申请会少
  • 原理剖析:说来惭愧,没看太明确,然而能够确定就是hold一段时间的点计算的
  • 感兴趣的能够本人钻研下D:workgo_workpkgmodgithub.comprometheusclient_golang@v1.9.0prometheussummary.go

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理