写点什么

Prometheus Native Histograms 实现原理及应用

  • 2022-11-10
    广东
  • 本文字数:3136 字

    阅读完需:约 10 分钟

Prometheus Native Histograms 实现原理及应用

最近 Prometheus 发布了 v2.40.0 版本,其中增加了 Native Histogram 的支持,这个功能的改动还是挺大的,从设计到开发完成历史 2 年多,下面我们就来一起看看它的实现原理和应用。

当前 Prometheus Histogram 的问题

在正式开始 Native Histogram 探究之前,先来看看当前 Prometheus 默认 Histogram 存在的问题。


当前 Prometheus Histogram 格式如下:



可以看到默认 Histogram 需要预先定义桶分布,默认值为(.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10),不同桶的值是累积的,每个桶都是单独指标,而且需要额外增加 'le="+Inf"','_count','_sum' 三个指标。由于这样的设计,从而导致如下问题:


  • 精度问题,由于需要业务提前定义桶分布,这个对开发来说有一定的心智负担,因为提前固化桶的分布,所以无法根据实际的请求值动态增加桶的数量,从而导致最终结果在某些范围(操作最大桶)出现较大偏差。

  • 存储问题:原始 Histogram 指标的不同桶以及'le="+Inf"','_count','_sum' 三个额外指标都属于不同时间线,需要单独存储与查询。

  • 不同桶的分布无法计算:同一指标不同实例之间的聚合,需要严格进行桶对齐及按照 le 相同的指标进行聚合,例如: histogram_quantile(__quantile__, sum by(le) (rate(__histogram_metric__[5m])))

Native Histogram 解决办法

针对以上问题,Prometheus 社区引入了 Native Histogram 方案(以前叫做 high sparse histograms 及高稀疏直方图),下面我们就来看看 Native Histogram 的实现原理。


注意,下面部分示例转载至 codesome 的分享.

固定桶的边界

在原始 Histogram 中,我们可以自定义任意桶分布,但在 Native Histogram 使用了固定桶边界的方案,用户只需要关心其解析度(相邻两个桶宽度比)即可计算出采用的是哪种种模式,故得出使用的是哪种固定桶边界方案,目前支持的桶边界固定分布方案一共有 12 种,它们的倍数因子分别为 2、4、16、256 以及 2 的(1/2)次方~2 的(1/256)次方,具体分布如下图:


(来源 codesome)

(来源 codesome)

这个参数对应 golang_client SDK 中的 NativeHistogramBucketFactor 参数,其实我们平时在使用的时候,并不需要关心底层具体使用的是哪种模式,只需要关心我相邻两个桶后面一个比前面一个增加多少即可,比如增加 10%,那么该值为 1.1 即可, SDK 会按照最接近这个比例匹配对应的固定桶分布。

数据编码

可以看到新的 Native Histogram 完全放弃了原始的编码格式,不再使用 'le="+Inf"','_count','_sum',而采用了固定桶分布的方式,所有我们可以给桶进行标号,只要我们知道其解析度就可以完全还原出所有编号对应的桶区间。



然后基于该桶编号进行 Native Histogram 数据编码:



这里我们首先记录 metadata 信息,包含解析度、sum、count 等信息,由于当解析度较小时,固定桶会比较多,可能出现大量桶无值的情况,这里增加了 Spans 概念,表示有值得的连续桶区间,这样只需要记录有值的桶即可。


因为单个 Native Histogram 只需要一个时间线进行存储了,所以最终对应的 chunk 编码结构如下:



这种编码带来的好处是 Prometheus TSDB Block Index 的减小,参考社区数据,在全是 Histogram 的情况下,索引文件达到 94% 减少:


实战练习

这里我们使用 prometheus/client_golang 构建 Demo App,定义一个 NativeHistogram 指标:


  httpDurationsHistogram: prometheus.NewHistogramVec(prometheus.HistogramOpts{      Name:                         "http_request_durations",      Help:                         "HTTP latency distributions.",      Buckets:                      prometheus.DefBuckets,      NativeHistogramZeroThreshold: 0.05,      NativeHistogramBucketFactor:  1.5,    }, []string{"service", "code"}),
复制代码


参数说明:


  • NativeHistogramZeroThreshold 用户 zero bucket 定义,及所有小于 0.05 的值都统计到该桶。

  • NativeHistogramBucketFactor 定义 NativeHistogram 桶的解析度,这里设置 1.5 表示期望在 1~2 之间再划分一下,用到的固定桶区间为 (1,1.414213562373095], (1.414213562373095,2]


针对 Native Histogram 通常我们只需要关心这两个参数,另外还有 NativeHistogramMaxBucketNumberNativeHistogramMinResetDurationNativeHistogramMaxZeroThreshold 这三个参数做更细粒度的配置。


这里我们显示设置 Buckets 为默认的配置,主要是为了兼容低版本的 Prometheus 抓取。

运行体验

该测试代码已经提交到 play-with-prometheus-native 仓库,大家可以使用如下命令运行测试程序


git clone https://github.com/grafanafans/play-with-prometheus-native.gitdocker-compose up -d
复制代码


该命令会启动 native-demo app, 监听 8080 端口,另外启动两个 Prometheus 实例进行指标抓取,一个使用 Native Histogram(监听 9090),一个使用原始 Histogram(监听 9091),具体 docker-compose.yaml 如下:


  prometheus:    image: prom/prometheus:v2.40.0    command:      - --config.file=/etc/prometheus/prometheus.yml      - --log.level=error      - --storage.tsdb.path=/prometheus      - --web.console.libraries=/usr/share/prometheus/console_libraries      - --web.console.templates=/usr/share/prometheus/consoles      - --enable-feature=native-histograms      - --enable-feature=exemplar-storage    volumes:      - ./config/prometheus.yml:/etc/prometheus/prometheus.yml      - prometheus-1-data:/prometheus    ports:      - 9090:9090  prometheus2:    image: prom/prometheus:v2.40.0    command:      - --config.file=/etc/prometheus/prometheus.yml      - --log.level=error      - --storage.tsdb.path=/prometheus      - --web.console.libraries=/usr/share/prometheus/console_libraries      - --web.console.templates=/usr/share/prometheus/consoles      - --enable-feature=exemplar-storage    volumes:      - ./config/prometheus.yml:/etc/prometheus/prometheus.yml      - prometheus-2-data:/prometheus    ports:      - 9091:9090  native-demo:    image: songjiayang/native-histogram-demo:v0.1.0    ports:      - 8080:8080    command:       - --native-factor=1.5      - --metrics-count=10
复制代码


我们可以修改 native-demo app 的运行参数,进行不同 NativeHistogramBucketFactor 测试。


当程序启动后,我们可以访问 http://localhost:9090/ 查看 Native Histogram 数据:



我们做了大量测试,目前版本对 Histogram 兼容性做的还不错,包括低版本 Prometheus 抓取带有 NativeHistogram 的数据,以及新版本 Prometheus 抓取老版本 Exporter 数据,它们都能正常抓取、存储、查询,其中包括了 exempalr 数据。


测试过程中我们也发现一些问题,比如同时做大量 series p95 计算的时候,Native Histogram 比原始 Histogram 耗时久一些,大约使其 2x-5x 的计算耗时,详情参考 issues/11548

总结

从设计到研发历时两年多,Prometheus 终于在 v2.40.0 发布了 Native Histogram 的第一个正式版本,它通过固定桶边界的方式,简单的配置即可在保证桶解析度的情况下,实现时间线存储的空间的极大优化。


新版本对老版本兼容支持做的也比较好,能够向后兼容,从个人试用结果看 Native Histogram 并非没有缺陷,比如大量时间线(1000+)同时计算 p99 耗时远远大于默认的 Histogram,而且目前它只支持 ProtoBuf 格式数据抓取,这给大家 Exporter 数据调试带来一定困扰。


但瑕不掩瑜,这个算是一个非常大的功能,感兴趣的小伙伴可以尝试起来。


更多文章,请关注我们公众号 【Grafana 爱好者】

发布于: 刚刚阅读数: 6
用户头像

学习是从了解到使用再到输出的过程。 2018-04-27 加入

GrafanaFans 是由南京多位 Grafana 爱好者一起发起的 Grafana 开源产品学习小组,致力于 LGTM(Loki、Grafana、Tempo、Mimir)技术栈在国内的普及和应用,欢迎关注开源项目 https://github.com/grafanafans/club。

评论

发布
暂无评论
Prometheus Native Histograms 实现原理及应用_云原生_Grafana 爱好者_InfoQ写作社区