3. Metric命名规范与标签设计:Prometheus命名最佳实践、标签(Label)的基数控制、避免标签爆炸的策略
指标(Metric)的命名与标签设计是可观测性工程的基石。一个设计良好的指标体系,能够显著降低告警噪声、提升排障效率;反之,命名混乱、标签失控的指标集,会迅速演变为“指标沼泽”,导致存储成本飙升、查询性能劣化、告警规则难以维护。本节将围绕 Prometheus 生态的命名最佳实践、标签基数控制以及标签爆炸的防御策略展开。
3.1 Prometheus 指标命名最佳实践
Prometheus 社区经过多年实践,形成了一套被广泛接受的命名规范。遵循这些规范,可以确保指标在跨团队、跨服务协作时具备一致的可读性和可发现性。
| 规范维度 | 要求 | 示例(正确) | 示例(错误) |
|---|---|---|---|
| 基础格式 | <namespace>_<subsystem>_<unit>[_<suffix>] |
node_memory_usage_bytes |
memory_used |
| 命名空间 | 使用应用或组件名作为前缀,避免全局冲突 | prometheus_http_requests_total |
http_requests_total |
| 单位后缀 | 明确标注单位(_bytes, _seconds, _total) |
api_request_duration_seconds |
api_request_time |
| 计数器后缀 | 计数器类型指标以 _total 结尾 |
http_requests_total |
http_requests_count |
| 布尔值 | 使用 _info 或 _enabled 后缀,值为 0/1 |
service_up, feature_enabled |
service_status(值为字符串) |
| 避免特殊字符 | 仅使用小写字母、数字和下划线 | db_query_latency_seconds |
DB.Query.Latency |
1. 指标名是否以应用/组件命名空间开头?
2. 是否包含了明确的单位后缀?
3. 如果是计数器,是否以
_total 结尾?4. 是否避免了驼峰、连字符、空格等非法字符?
3.2 标签(Label)的基数控制
标签是 Prometheus 数据模型的灵魂,它赋予了指标多维度的可分类能力。然而,标签的“基数”(Cardinality)——即某个标签取值的数量——是决定系统稳定性的关键因素。高基数标签是导致 TSDB 性能退化、内存溢出、查询超时的首要元凶。
3.2.1 基数分类与风险等级
| 基数等级 | 取值数量级 | 典型示例 | 风险等级 |
|---|---|---|---|
| 低基数 | 1 ~ 100 | method(GET/POST/PUT/DELETE) |
安全 |
| 中基数 | 100 ~ 10,000 | status_code(200/404/500 等) |
需关注 |
| 高基数 | 10,000 ~ 1,000,000 | user_id, request_id, ip_address |
危险 |
| 超高基数 | > 1,000,000 | trace_id, session_id, email |
禁止 |
3.2.2 基数爆炸的连锁反应
当一个指标携带多个高基数标签时,其总时间序列数呈笛卡尔积增长。例如:http_requests_total{method="GET", user_id="12345", endpoint="/api/v1/orders"},若 method 有 4 种、user_id 有 100 万、endpoint 有 100 个,则理论时间序列数为 4 × 1,000,000 × 100 = 4 亿。这将对存储、内存、网络和查询性能造成毁灭性打击。
3.3 避免标签爆炸的策略
标签爆炸(Label Explosion)是指由于设计不当,导致指标的时间序列数量失控增长的现象。以下策略可以帮助你在设计阶段就有效防御标签爆炸。
3.3.1 标签设计四步检查法
- 必要性检查: 这个标签是否会被告警规则、Dashboard 面板或排障查询直接使用?如果答案是否定的,删除它。
- 基数评估: 该标签的取值数量级是多少?是否属于高基数或超高基数?如果是,寻找替代方案。
- 聚合可行性: 能否通过聚合操作(如
sum by)在查询时获得所需信息,从而避免在采集时保留高基数标签? - 替代方案: 是否可以将高基数信息转移到日志或追踪系统?是否可以使用
info类型的指标来关联元数据?
3.3.2 常用降基技术
| 技术 | 描述 | 适用场景 |
|---|---|---|
| 标签降维 | 将多个低基数标签组合成一个枚举标签 | 如将 region 和 az 合并为 location |
| 分桶(Bucketing) | 将连续值或高基数离散值映射到有限个桶中 | 如将 user_id 按范围分桶:user_bucket="0-999" |
| 采样与聚合 | 在采集端预先聚合,只保留聚合后的指标 | 如使用 Recording Rules 预计算 rate 或 avg |
| Info 指标关联 | 使用 _info 指标存储静态元数据,通过 group_left 关联 |
如 machine_info{hostname="...", rack="...", team="..."} |
| 标签白名单 | 在 Exporter 或 Agent 侧配置只保留指定的标签 | 如 Prometheus metric_relabel_configs 中的 keep 动作 |
3.3.3 实战:使用 Relabeling 防御标签爆炸
Prometheus 提供了强大的 Relabeling 机制,可以在采集阶段对标签进行过滤、修改和删除。以下是一个典型的防御配置示例:
scrape_configs:
- job_name: 'example-app'
static_configs:
- targets: ['localhost:8080']
metric_relabel_configs:
# 1. 删除已知的高基数标签
- source_labels: [user_id, request_id, session_id]
regex: '.+'
action: drop
# 2. 只保留白名单中的标签
- source_labels: [__name__]
regex: 'http_requests_total'
action: keep
# 3. 将连续值标签分桶
- source_labels: [response_time_ms]
target_label: response_time_bucket
replacement: '${1}'
regex: '(\d+)'
- source_labels: [response_time_ms]
target_label: response_time_bucket
replacement: 'gt_1000'
regex: '1\d{3,}'
action: replace
metric_relabel_configs 作用于指标级别的标签,是防御标签爆炸的最后一道防线。action: drop 用于直接丢弃包含高基数标签的整个指标;action: keep 用于只保留符合特定模式的指标;action: replace 配合正则替换,可以实现标签值的分桶或重映射。
3.4 标签设计决策树
在实际工作中,面对一个待添加的标签,可以参照以下决策树快速判断其合理性:
3.5 总结与行动清单
- 命名即文档: 遵循
namespace_subsystem_unit[_suffix]格式,让指标名自解释。 - 标签是双刃剑: 提供维度的同时,也引入了基数风险。每次添加标签前,必须评估其基数。
- 防御优先于治理: 在 Exporter 或 Agent 侧通过 Relabeling 配置白名单、黑名单和分桶规则,从源头阻止标签爆炸。
- 持续监控: 使用
prometheus_tsdb_head_series等内置指标监控时间序列总量,设置告警阈值(如单指标超过 10 万序列时告警)。 - Info 指标模式: 对于静态或低频变化的元数据(如机房位置、应用负责人),使用
_info指标存储,避免将其作为高基数标签附加到每个指标上。