核心思路是数据写入内存前拦截过滤聚合;先查prometheus_tsdb_head_series和count by (__name__)识别高基数指标,再通过label_values定位url、user_id等动态标签爆炸点,继而在scrape阶段用labeldrop裁剪、正则归一化路径、禁用高危元数据标签,并用recording rules预聚合替代原始高基数查询,最后设max-series-per-metric限制与定期清理兜底。

核心思路是:在数据写入内存前就拦截、过滤、聚合,而不是等它进来再清理。
识别高基数指标源头
先确认哪些指标正在制造海量时间序列:
- 查
prometheus_tsdb_head_series指标,观察 Head Block 中活跃序列数是否持续上涨 - 用
count by (__name__) ({__name__=~".+"})查各指标的序列数量,重点关注带url、path、user_id、request_id、trace_id等动态标签的指标 - 检查
label_values(__name__, "xxx")(如label_values(http_request_duration_seconds_bucket, url)),看某个标签值是否已达数千甚至上万
从采集端做标签裁剪
在 scrape 阶段就丢弃危险标签,避免它们进入 TSDB:
- 在
relabel_configs中用action: labeldrop移除高基数标签,例如:- source_labels: [url] action: labeldrop- source_labels: [user_id] action: labeldrop - 对必须保留的路径类信息,做正则归一化,比如把
/api/v1/users/12345改成/api/v1/users/:id,再用labelmap或replacement覆盖原标签 - 禁用默认注入的高风险元数据标签,如
__meta_kubernetes_pod_uid、__meta_kubernetes_pod_ip,除非业务强依赖
用 recording rules 替代原始指标
不直接查询爆炸性原始指标,而是预计算聚合结果:
- 将
http_requests_total{method, status, url}拆成两层:
• 一层按method和status聚合(低基数)
• 另一层只保留关键url白名单(如/health、/login) - 定义 recording rule:
job:http_requests_total:rate5m = sum by (job, method, status) (rate(http_requests_total[5m])) - 后续所有告警和看板都基于这类聚合指标,原始高基数指标可设为
drop或仅保留短期用于调试
限制与兜底机制
防止某次配置遗漏或新服务上线引发连锁反应:
- 在 Prometheus 启动参数中设置
--storage.tsdb.max-series-per-metric=50000,超限后自动拒绝该指标写入 - 启用
--web.enable-admin-api并配合定期脚本调用/api/v1/admin/tsdb/delete_series清理已确认无用的系列(慎用,需配合时间范围) - 对 exporter 自身做改造:比如 Kafka exporter 不暴露每个 partition 的 lag,而是聚合为 group-level 或 topic-level 指标











