Prometheus 高基数 label 导致 TSDB 查询变慢、内存暴涨,怎么定位与治理?
该问题的正文、回答与评论。
【背景】 我做了 10 年运维,目前维护一个中型生产集群:Kubernetes 1.28.9 + containerd 1.7.13,监控栈是 Prometheus 2.51 单实例(16C32G,本地 NVMe 500GB),scrape 目标约 1200 个,总 series 数从半年前的 80 万涨到了 240 万,其中 60% 来自一个 Java 网关的 http_server_requests 指标(label 里带 user_id + endpoint + status_code)。
【现象】 1)Prometheus 内存从 6G 涨到 22G,/api/v1/status/runtime 里 TSDB 的 head series 占了大头; 2)Grafana 上一些简单的 rate() 查询 P95 从 200ms 涨到 3.5s,偶发 10s+; 3)TSDB compaction 日志频繁出现 "block write failed",WAL 偶尔堆到 8G; 4)业务侧没动,纯粹是流量和并发用户数在涨。
【已尝试的排查与结果】
- 跑了
topk(20, count by (user_id) (http_server_requests_total)),确认 user_id 基数已经 1.8 万; - 查了 Prometheus 的 /metrics,
prometheus_tsdb_head_series= 241 万,prometheus_tsdb_storage_retention_time_bytes接近上限; - 试过把 retention 从 30d 缩到 14d,内存峰值从 22G 降到 15G,但查询还是慢,说明不是 retention 的问题;
- 怀疑是用户维度 label 把 series 撑爆了,但没把握——因为 endpoint 才 40 个、status_code 才 5 个,单看都不高,是 user_id 把它们乘起来的。
【期望得到的答案形态】 希望有同样踩过这个坑的同学给一套可复现的"定位 → 治理"清单,具体包括: 1)怎么在不动业务代码的前提下,先量化每个 label 的基数贡献(有没有比 topk 更准的方法,比如用 promtool tsdb dump 或 recording rule 做基数估算); 2)user_id 这种高基数 label,业界常见的 3 种治理路径——改代码去 label / 用 recording rule 预聚合 / 上 Thanos/Mimir 分层——各自的取舍和迁移成本; 3)如果短期只能"压症状",Prometheus 侧有哪些参数值得调(--tsdb.head-block-ratio、--max-chunk-count?),以及调完怎么验证; 4)有没有一个"高基数 label 黑名单"的告警规则可以加到 Alertmanager,让以后新上的高基数指标能被提前拦住?
最好能贴一下你们线上的真实配置片段(脱敏后),方便我直接抄。