Prometheus v2.44.0 开始,默认采用 stringlabels 存储 Labels,它能够降低 CPU 和内存(-20%)的使用,下面我们就来看看它是如何做到的?
先聊聊 slicelabels
我们都知道 Prometheus 的指标是由一系列标签对(Labels)组成的,例如:
request_count{job="web", route="query_range"}
在过去 Prometheus 版本中, Labels 是使用字符串数组(slicelabels)来存储,所有标签对按照 [key,value] 排序,然后 append 到同一 slice ,结构大致为:
这种存储方式虽然代码逻辑简单,但其内存利用率并不高,因为除了要存储 k/v 本身内容外,还增加了 slice 头(24 字节)和每个字符串头(16 字节)的额外开销。
例如一个包含 10 个键值对,所有 k/v 都是 10 个字节的指标,使用 slicelabels 需要的内存大致为 24+102(16+10)=544 字节,但我们标签实际内容总共才 21010=200 字节。
可见 slicelabels 并不是一种非常经济的存储方案,所以才有了 stringlabels。
stringlabels 如何解决问题
stringlabels 放弃了 slice 存储多个字符串,而采用单一字符串来存储整个 Labels,结构大致为:
每个 k/v 字符串,先记录其长度(length prefix),然后再追加具体内容,最终统一编码到同一字符串,如果想查找/获取某些标签对,都需要从头解析获取。
这种编码方式内存开销为 string 固定 16 字节头和 每个k/v 的长度标记(varint)以及具体的 k/v内容,还是上面样例,使用 stringlabels 所占内存可以减小为 16+10*2(1+10)=236,不到 slicelabels 的50%。
相关核心代码都在 model/labels/labels_string.go 文件中。
测试使用
这里使用 avalanche 来构建测试 metrics,并分别用两个 不同版本的 Prometheus 抓取对应指标,过一段时间后,可以发现 v2.44.0 版本所占内存(默认开启 stringlabels)为 v2.43.0(非 stringlabels )80% 不到。
相关配置如下:
- docker-compose.yaml
version: '3.4'
services:
prometheus:
image: prom/prometheus:v2.43.0
user: root
command: ["--config.file=/etc/prometheus/prometheus.yaml"]
volumes:
- ./prometheus.yaml:/etc/prometheus/prometheus.yaml
- ./data/data-prometheus-1:/prometheus
ports:
- 9090:9090
prometheus2:
image: prom/prometheus:v2.44.0-rc.0
user: root
command: ["--config.file=/etc/prometheus/prometheus.yaml"]
volumes:
- ./prometheus.yaml:/etc/prometheus/prometheus.yaml
- ./data/data-prometheus-2:/prometheus
ports:
- 9091:9090
avalanche:
image: prometheuscommunity/avalanche:main
command:
- --metric-count=1000
ports:
- 9001:9001
- prometheus.yaml
global:
scrape_interval: 15s
scrape_configs:
- job_name: avalanche
static_configs:
- targets: ['avalanche:9001']
- job_name: prometheus
static_configs:
- targets: ['prometheus:9090', 'prometheus2:9090']
总结
Prometheus 的 stringlabels 带来了不错的内存节省,随着作为即将发布的Prometheus v2.44.0 的默选项,标志着它日趋稳定,大家可以体验起来了。