指标基数问题与解决
一个团队的 Prometheus 在运行 6 个月后开始变慢,查询延迟从毫秒级上升到分钟级。工程师检查了数据量和存储空间,都没有问题。最后定位到根因:某个服务在标签中使用了 user_id,而有 200 万用户,导致这个指标产生了 200 万条时间序列。
这就是 Cardinality(基数)问题——Prometheus 的克星。基数爆炸会让查询变慢、内存耗尽、写入阻塞,严重时会导致 Prometheus OOM。
什么是基数
基数(Cardinality) 是一个标签组合能产生的不同值的数量。
时间序列数量 = 指标数量 × 所有标签基数的乘积。
为什么高基数是问题
内存爆炸
Prometheus 的内存使用与时间序列数量成正比。每个时间序列需要在内存中维护:
- 索引数据(标签组合 → 序列 ID)
- Head Block 数据(最新样本)
- 查询结果缓存
查询性能退化
写入压力
每个新的标签组合都需要在内存中创建新的时间序列对象:
常见的高基数场景
场景一:用户级标签
场景二:动态 URL 路径
场景三:IP 地址和会话 ID
诊断高基数
检查高基数指标
Prometheus 诊断端点
解决方案
方案一:使用 relabel_configs 丢弃高基数字段
prometheus.yml
方案二:用 Histogram 的 buckets 替代精确标签
方案三:OTel SDK 层面的基数控制
OtelConfig.java
方案四:数据分层存储
thanos-sidecar.yml
预防措施
建立基线
prometheus-baseline.yml
命名规范
质量判断标准
读完本节后,你应该能够回答:
- 什么是基数(Cardinality)?为什么高基数会导致 Prometheus OOM 和查询超时?
- 哪些场景是常见的高基数陷阱?请列举至少 3 个,每个场景给出正确和错误的做法。
- 如何诊断 Prometheus 中的高基数问题?有哪些具体的 PromQL 查询和诊断端点可以使用?
- 在 OTel SDK 层面,如何通过 View API 控制 Histogram 的标签数量?
- 如何在团队中建立基线检查机制,防止新引入的代码导致基数爆炸?