监控指标别被标签拖垮:Prometheus 基数治理实战

Prometheus 指标基数治理

监控系统开始变慢时,团队常先增加内存,却忽略了真正的放大器:标签。一个看似普通的 user_id,可能把一条指标扩成百万条时间序列。治理基数不是少采指标,而是让指标只承担聚合判断,把细节留给更合适的信号。

基数为什么会失控

Prometheus 用“指标名 + 完整标签集合”识别一条时间序列。标签取值组合越多,内存、索引、持久化与查询成本就越高。若某指标有 5 个方法、6 类状态、80 条路由和 20 个实例,理论上最多会形成:

1
5 × 6 × 80 × 20 = 48,000 条时间序列

实际维度可能相关,因此乘积只是上界;但用户 ID、请求 ID、原始 URL 等无界值会持续制造新组合。经典直方图还会为每个标签组合产生多条 bucket 以及 _sum_count,放大尤其明显。

标签组合造成时间序列膨胀

标签 建议 原因
methodstatus_class 保留 枚举有限,便于聚合
规范化的 route 谨慎保留 路由数量应有明确上限
user_idrequest_id 禁止 取值近乎无界
含参数的原始路径 禁止 每个资源都可能生成新序列

埋点时应记录 route="/orders/:id",而不是 /orders/84721。判断某字段是否适合作标签,可以问一句:半年后它可能出现多少种值?回答不出上限,就不应进入指标。

先找到增长源

先观察 prometheus_tsdb_head_series 的趋势,再用 TSDB 状态页查看高基数指标和标签。临时排查也可按指标名统计当前序列数;查询本身可能较重,应避开高峰并限制使用频率:

1
topk(20, count by (__name__) ({__name__=~".+"}))

发现异常指标后,回到发布记录与埋点代码,比较新增标签前后的序列增量。不要只看总量:实例扩容也会推高序列数,但增长应与实例数近似成比例;若流量不变却持续上升,通常是无界标签或不断出现的新路径。

让三类信号各司其职

指标、日志与追踪的信号分工

指标适合回答“错误率是否升高”,日志适合查询某个用户或请求的事件,追踪适合还原一次调用链。需要关联时,在日志和追踪中保存请求 ID;指标只保留服务、规范化路由、结果类别等有界维度。不要为了从告警直接定位单个请求,把所有上下文塞进标签。

从源头修复并设置护栏

最有效的修复是在埋点端删除危险标签、规范化路由,并减少不必要的直方图桶。聚合规则能让看板更快,却不会删除已采集的原始序列,因此不能替代源头治理。

紧急止损时,可以在采集前丢弃明确无用的实验指标,并设置单次采集样本上限:

1
2
3
4
5
6
7
scrape_configs:
- job_name: app
sample_limit: 50000
metric_relabel_configs:
- source_labels: [__name__]
regex: "experimental_.*"
action: drop

sample_limit 超限会使整次采集失败,它是保险丝而不是日常配额,必须同时告警。删除标签也要小心:不同序列去掉标签后可能发生碰撞,优先修改生产者或整条丢弃确认无用的指标。

把基数纳入变更评审

为关键指标维护一份简单清单:负责人、标签集合、每个标签的预期上限、预计组合数。新增标签时先在预发或少量实例启用,观察 Head Series、内存、WAL 写入和采集耗时,再逐步放量。

检查项 通过标准
标签边界 每个值域可解释、可估算
增量预算 新增序列没有超过团队预算
回滚手段 能关闭新指标或撤销新标签
责任归属 异常增长能找到维护者

基数治理的核心不是追求序列越少越好,而是让每条序列都有明确价值和成本边界。先修复一个增长最快的指标,再把预算检查放进埋点评审,通常比反复扩容更持久。