高基数问题以及如何解决?
背景
近期发现自己实验用的 Prometheus 性能出现瓶颈, 经常会出现如下告警:
PrometheusMissingRuleEvaluations
PrometheusRuleFailures
之后慢慢排查发现是由于 Prometheus 的某些 series 的高基数(High Cardinality)导致的. 本文是对 Prometheus 高基数问题的一次全面总结.
什么是基数(Cardinality)?
基数的基本定义是指一个给定集合中的元素的数量。
在Prometheus和可观察性的世界里,标签基数是非常重要的,因为它影响到你的监控系统的性能和资源使用。
下面这张图, 可以清晰地反应基数的重要性:

简单地说。基数 是指一个标签的总体数值的计数。在上面的例子中,标签status_code
的基数是5,(即:1xx
2xx
3xx
4xx
5xx
),environment
的基数是2(即prod
dev
),而指标server_responses
的总体基数是10。
多少算高基数?
一般来说:
较低的基数 1:5的标签值比率,
标准基数 1:80的标签值比率
高基数 1:10000的标签值比率。
还是上面的例子, 如果 status_code
是详细的code, 如200
404
..., 那它的基数就可能高达数百个, environment
的基数再多一些, 指标server_responses
的总体基数就会迅速膨胀.
高基数的典型案例
这还不够形象, 再举 2 个特别典型的例子:
有一个指标叫做:
http_request_duration_seconds_bucket
小规模也会有:
100*10*400*5=2 000 000
200万个 series 💀💀💀如果大规模, url 近乎无穷的话, 那么这个基数根本无法计算出来💥💥💥
即使规模很小, url 可能也会有 400 个 url
这里还有个特别恐怖的隐患, 就是对于大规模系统来说, 这个 url 可能是近乎于无穷!!!
它有
instance
label, 对应 100 个实例;有
le
label, 对应的是不同的 buckets, 有 10 个 buckets, 如(0.002
0.004
0.008
...=+inf
)它还有
url
这个 label, 对应的是不通的 url:它还有
http_method
这个label, 对应有 5 个 http method在这种情况下, 该指标的 label
再有一种情况, 将
user_id
甚至是session_id
经纬度
这种本来基数就很大, 甚至可能是无穷的参数设为 label, 那么对于 Prometheus 来说就是灾难了.💥💥💥
高基数的负面影响
当 Prometheus 有高基数的时候,就会出现各种问题:
监控系统不稳定甚至崩溃
仪表板加载很慢甚至加载失败
监控查询很慢甚至失败
计算存储资源开销巨大
监控充斥着大量噪音干扰
SRE 团队不得不疲于应对海量的告警数据, 反而耽误 root cause 的分析定位
📝Notes:
基数 与指标系列(metrics series) 的数量相对应。所以在这篇博文中,会把 series 的数量与基数交替提及。
如何分析高基数问题?
分析高基数问题有以下方法:
使用 Prometheus UI 分析
使用 Prometheus PromQL 分析
使用 Prometheus API 分析
使用 Grafana Mimirtool 分析未使用的指标
使用 Prometheus UI 分析
从 Prometheus v2.14.0 以后, 在 UI 上直接有 Head Cardinality Stats 这个菜单. 极大方便了我们进行高基数问题的分析! 👍️👍️👍️
位于: Prometheus UI -> Status -> TSDB Status -> Head Cardinality Stats, 截图如下:
📝Notes:
以下截图的系统规模说明: 这就是个我用来做实验的环境, 只有 4 个 1c2g 的 node


从上图可以直观看到:
值最多的 Label 是
url
最多的 series 的指标有:
apiserver_request_duration_seconds_bucket
45524rest_client_rate_limiter_duration_seconds_bucket
36971rest_client_request_duration_seconds_bucket
10032内存使用量最多的 Label:
url
根据 Label 键值对匹配, series 最多的键值对有: (这一项目前对我来说用处不大)
endpoint=metrics
105406service=pushprox-k3s-server-client
101548job=k3s-server
101543namespace=cattle-monitoring-system
101120metrics_path=/metrics
91761
使用 Prometheus PromQL 分析
如果 Prometheus 版本低于 v2.14.0, 那就需要通过:
Prometheus PromQL
Prometheus API
来进行分析.
以下提供一些实用的 PromQL:
topk(10, count by (__name__)({__name__=~".+"}))
对应的查询结果就是上文的 series 指标最多的 Top10
知道了 Top10, 接下来可以进一步查询细节, 由于基数巨大, 如果查询 range 可能会一直失败, 所以推荐使用 instant
的方式查询细节.
如果要查询标签的维度, 可以执行如下 PromQL:
count(count by (label_name) (metric_name))
如:
count(count by (url) (apiserver_request_duration_seconds_bucket))
另外还有一些其他的 PromQL, 罗列如下:
sum(scrape_series_added) by (job)
通过 job Label 分析 series 增长sum(scrape_samples_scraped) by (job)
通过 job Label 分析 series 总量prometheus_tsdb_symbol_table_size_bytes
使用 Prometheus API 分析
因为高基数问题的特点, 所以通过 Prometheus PromQL 查询可能经常会超时或失败. 那么可以通过 Prometheus API 进行分析: