指标基数膨胀问题与Prometheus存储压力
在网站运维的监控体系中,Prometheus指标基数(Cardinality)失控是最常见且最具破坏力的问题之一。当某个指标的标签组合产生过多唯一时间序列时,Prometheus的内存占用、磁盘写入和查询延迟会急剧恶化。一个包含用户ID、请求路径、状态码等多维度标签的指标,基数可轻易膨胀至数百万,使Prometheus实例OOM崩溃。
指标基数等于该指标所有标签的笛卡尔积。例如http_requests_total包含method(5值)、path(2000值)、status_code(10值)、user_id(50000值)四个标签,基数=5x2000x10x50000=50亿。这远超Prometheus单实例承载能力。
基数膨胀的典型症状:
1. Prometheus内存持续增长,RSS超过配置的storage.tsdb.max-block-chunk-pool-size
2. TSDB Head加载耗时从秒级增长到分钟级
3. /api/v1/query查询延迟从百毫秒增长到数十秒
4. WAL日志文件体积异常膨胀
基数检测与高基数指标定位
治理基数膨胀的第一步是定位问题指标。Prometheus内置了基数分析接口:
# 查询所有指标的时间序列数量
curl http://localhost:9090/api/v1/label/__name__/values
# 查看TSDB统计信息
curl http://localhost:9090/api/v1/status/tsdb
# 使用PromQL统计每个指标的时间序列数量
topk(20, count by (__name__)({__name__=~".+"}))
更精准的分析工具:
# 使用promql-cardinality-exporter导出基数指标
docker run --rm -p 9798:9798 \
-e PROMETHEUS_URL=http://prometheus:9090 \
grafana/promql-cardinality-exporter
# 在Grafana中构建基数监控面板
topk(20, count by (__name__)({__name__=~".+"}))
定位高基数指标后,分析其标签组合:是否有标签值无限增长(如user_id、request_id、session_id),这些是基数爆炸的直接原因。
Cardinality Limiter限流配置实战
解决基数膨胀最有效的手段是在数据采集源头限流。
方案1:Prometheus Server端限制
# prometheus.yml
global:
scrape_interval: 15s
# TSDB配置
storage:
tsdb:
max-block-chunk-pool-size: 10GB
# 运行时限制
--storage.tsdb.max-exemplars=100000
--query.max-samples=50000000
方案2:使用Grafana Mimir限制基数
# Mimir cardinality limiter 配置
limits:
max_global_series_per_user: 500000
max_series_per_metric: 50000
max_label_names_per_series: 30
cardinality_limit_action: reject
方案3:SDK端基数限制(推荐)
在应用SDK中直接限制标签值空间,将高基数标签转换为低基数桶:
import prometheus_client as prom
# 反面案例:直接用高基数标签
# http_requests = prom.Counter(
# "http_requests_total",
# "HTTP requests",
# ["method", "path", "user_id"] # user_id是无限值
# )
# 正面案例:将高基数标签替换为桶
http_requests = prom.Counter(
"http_requests_total",
"HTTP requests",
["method", "path_group", "status_class"] # 有限值
)
def path_group(path):
if path.startswith("/api/v1/users/"):
return "/api/v1/users/:id"
elif path.startswith("/api/v1/orders/"):
return "/api/v1/orders/:id"
return path
Recording Rules降采样与聚合策略
对历史高基数数据,通过Recording Rules预聚合降低查询时基数:
# recording_rules.yml
groups:
- name: http_aggregation
interval: 30s
rules:
- record: http:request_rate:5m
expr: sum(rate(http_requests_total[5m])) by (method, status_class)
- record: http:request_rate_by_service:5m
expr: sum(rate(http_requests_total[5m])) by (service, method)
- name: infra_aggregation
interval: 1m
rules:
- record: cluster:cpu_usage:ratio:5m
expr: avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) by (cluster)
预聚合后的指标基数从数十万降到数百,查询性能提升10-50倍。代价是丢失细粒度信息,因此需保留原始指标的短期存储,预聚合指标用于长期保留和告警。
Relabeling在采集端丢弃高基数标签
Prometheus的metric_relabel_configs可以在采集时丢弃或替换高基数标签:
# prometheus.yml
scrape_configs:
- job_name: "myapp"
static_configs:
- targets: ["localhost:8080"]
metric_relabel_configs:
- source_labels: [user_id]
regex: ".+"
action: labeldrop
- source_labels: [path]
regex: "/api/v1/users/.+"
target_label: path
replacement: "/api/v1/users/:id"
action: replace
- source_labels: [__name__]
regex: "http_requests_with_trace_total"
action: drop
这套治理体系从SDK端限制、采集端过滤、存储端限流、查询端聚合四个层面控制指标基数,是大规模SRE稳定性工程的标配方案。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/prometheus-zhi-biao-ji-shu-peng-zhang-zhi-li-yu/