怎么定义指标 · 命名、标签与基数红线

知道该测什么之后,怎么把它定义好?这一章给可执行的规则:命名规范(单位后缀是硬要求)、标签设计的取舍,以及那条必须守住的基数红线——含一个能让你立刻算出「这个指标要花多少钱」的公式。

作者 David更新于 第 17 篇(共 52 篇)

怎么定义指标 · 命名、标签与基数红线

方法论告诉你该测什么,这一章讲怎么把它定义好。这是最“手艺活”的一章:规则不多,但每条都是血泪换来的——定义指标是个几乎不可逆的决定,一旦上线、被面板和告警依赖,改名字就是一场迁移。

命名规范

Prometheus 的命名约定不是风格问题,有些是硬要求:

格式:<命名空间>_<名词>_<单位>_<后缀>

http_requests_total                    ← counter 必须 _total
http_request_duration_seconds          ← 单位必须写全:seconds 不是 ms
node_memory_usage_bytes                ← bytes 不是 MB
process_cpu_seconds_total              ← 累计 CPU 秒数
queue_depth                            ← gauge 不加 _total

规则清单:

规则为什么
counter 以 _total 结尾OpenMetrics 硬性规定,工具链依赖它
单位写在名字里,且用基本单位seconds 不用 milliseconds,bytes 不用 MB
单位用复数_seconds、_bytes(约定)
同名指标单位必须一致否则聚合出来是垃圾
用 _ 不用驼峰全生态约定
前缀用命名空间http_、node_、myapp_——避免撞名

“用基本单位”这条被低估了。 为什么不用毫秒?因为你会忘。三个月后你看到 http_request_duration = 250,这是 250 毫秒还是 250 秒?名字里带单位,读的人永远不用猜。而统一成秒(而非各处混用 ms/s/μs),让跨指标的运算永远正确——你可以直接把两个 _seconds 相加,不用转换。Grafana 会负责把 0.25 显示成 “250ms”,展示是展示的事,存储永远用基本单位。

一个真实的坑:改指标名的代价极高。 指标名一旦上线,就被写进了:面板、告警规则、recording rules、别人的看板、别人的告警、事故复盘文档、甚至某些自动化脚本。改名意味着所有这些都要同步改,而且历史数据不会跟着改名——你会得到一条在某个时间点断掉的曲线,以及一条从那时才开始的新曲线。

所以:第一次就想清楚。 上线前花十分钟对着这张表检查一遍,比日后迁移省一百倍力气。

标签设计:自由度与成本的交易

标签是提问的自由度,但每个标签都在放大 series 数。设计标签就是在做这笔交易。

三个问题,决定一个标签该不该加:

  1. 我会用它来筛选或聚合吗? —— 如果你想不出一个具体的查询会用到它,别加。“以后可能有用”是基数爆炸最常见的借口。
  2. 它的取值数量有上界吗? —— status(约 10 种)、method(约 8 种)有界;user_id、url、error_message 无界。无界的绝对不能加。
  3. 它和别的标签正交吗? —— 如果 region 和 datacenter 一一对应,加两个是浪费(series 数不变但存储翻倍)。

一个好用的经验法则:如果你无法在心里数出这个标签有多少种取值,它就不该是标签。

基数红线:一个能立刻算出成本的公式

这是本章最实用的东西。

series 数 = 各标签取值数的乘积** × 实例数。这是乘法,不是加法——这就是它会爆炸的原因。**

举例,一个 HTTP 服务的 RED 指标:

http_request_duration_seconds_bucket{method, route, status, le, instance}

method:    5   (GET/POST/PUT/DELETE/PATCH)
route:    50   (50 个路由)
status:    8   (200/201/400/401/403/404/500/503)
le:       12   (11 个桶 + Inf)  ← 别忘了 histogram 的桶也是标签!
instance: 20   (20 个 pod)
─────────────────────────────────────
series 数 = 5 × 50 × 8 × 12 × 20 = 480,000 条

48 万条 series,一个指标。 而 Prometheus 的经验值是:每条活跃 series 大约吃 1~3 KB 内存(索引 + 标签 + 未落盘的 chunk)。

480,000 × 2KB ≈ 1 GB 内存   ← 就这一个指标

现在看看加一个“无界”标签会怎样:

如果 route 用了原始 path(带订单 ID),假设一天 10 万个不同订单:
series 数 = 5 × 100,000 × 8 × 12 × 20 = 960,000,000 条
≈ 1.9 TB 内存 → Prometheus 当场 OOM

这就是第 1 部分那个 scrape_series_added 持续 > 0 的后果。记住这个乘法公式,埋点前算一遍——它只需要 30 秒,能救你一次线上事故。

自己拖一下

光看公式没有手感。拖动下面的滑块,看这个乘法怎么把你送进 OOM:

拖动滑块,看这个乘法怎么爆炸
5
50
8
12
20
5 × 50 × 8 × 12 × 20 = 480,000 条 series
48.0 万条 series≈ 938 MB内存(按 2 KB/series)要留神
1 千10 万1000 万10 亿

单实例还扛得住,但该盯 head_series 了。

注意刻度是对数的——把 route 从 50 拖到 200,条子跳过大半个量程。这就是 「乘法,不是加法」的手感:直觉说「就多几个路由」,而现实是整体 ×4。

绝对不能进标签的东西

黑名单(记住它):

❌ 绝不为什么该放哪
user_id、session_id、request_id、trace_id无界日志 / 追踪
原始 URL path(带 ID)无界用路由模板 /orders/:id
error_message、异常堆栈无界 + 长日志
时间戳、日期无界时间是 series 的维度,不是标签
email、IP(客户端的)无界日志
完整的 SQL 语句无界日志 / APM

共同点:它们都是“标识符”或“自由文本”,不是“类别”。 一个可靠的判据:标签的取值应该是一个你能提前列举的、有限的“枚举”。status 是枚举,user_id 不是。

“时间戳当标签”这条值得特别提醒——听起来很蠢没人会犯,但它会伪装着出现:比如把 version="2026-07-15-build-1234" 当标签,每次发布就产生一批新 series;或者 batch_id、job_run_id 这类每次运行都变的东西。任何“每次都不一样”的值,都是时间戳的变体。

那些“确实需要高基数”的场景怎么办

如果你真的需要按 user_id 查询,答案是:别用指标。 这不是妥协,而是用对工具:

  • 要按用户查 → 日志(Loki/ES)或追踪。它们为高基数而生:Loki 只索引少量标签,内容全文检索;追踪本来就是一条条独立的记录。
  • 要看“最差的那些用户” → exemplar:指标里挂几个样例 trace_id,从曲线一键跳到那条具体请求。这是“聚合 + 样例”的混合模式,鱼和熊掌兼得的正确姿势。
  • 要看 Top-N(如最慢的 10 个接口) → 这个可以用指标,因为接口数是有界的。但“最慢的 10 个用户”不行。
  • 确实要高基数指标 → 这时该考虑的是专门的高基数后端(如某些商业 APM),而不是硬塞进 Prometheus。Prometheus 的整个设计(倒排索引、内存里的 Head)都建立在“series 数有界”这个假设上,你打破它就是在跟它的架构对抗。

一个 checklist

埋点前逐条过一遍,30 秒:

□ 名字符合规范?(_total 后缀、单位写全、基本单位、命名空间前缀)
□ 类型选对?(会减少的不是 counter;要看分布用 histogram 不用 summary)
□ 每个标签我都能说出一个会用到它的具体查询?
□ 每个标签的取值数我都能数出来?(数不出来 = 别加)
□ 算过乘法了?(各标签取值数 × 实例数 → 心里有数)
□ histogram 的桶数算进去了?(桶也是标签!)
□ 桶边界覆盖了我的 SLO 阈值?
□ 黑名单里的东西一个都没混进来?

小结与下一章

命名:<命名空间>_<名词>_<单位>_<后缀>——counter 必须 _total、单位写进名字且用基本单位(seconds 不用 ms,因为你会忘;统一基本单位让跨指标运算永远正确,展示交给 Grafana);改名代价极高(面板/告警/规则/文档全要改,历史数据还不跟着改名),第一次就想清楚。标签设计问三个问题:会用它筛选吗、取值有上界吗、和别的标签正交吗——数不出取值数的东西不该是标签。基数红线:series 数 = 各标签取值数的乘积 × 实例数(是乘法!),每条活跃 series 约 1~3 KB 内存——一个普通 RED 指标就 48 万条 ≈ 1GB;换成原始 path 就是 9.6 亿条 ≈ 1.9TB 当场 OOM。黑名单:user_id / 原始 path / error_message / 时间戳及其变体(version、batch_id 等“每次都不一样”的值)一律不进标签。真需要高基数?别用指标——用日志 / 追踪 / exemplar。

规范有了,但还有个最实际的问题:多少算正常? 下一章给具体数字,收尾第 2 部分。👉 常见指标的合理范围。

参考资料

  • [1] Prometheus 官方文档:Metric and label naming、Best practices — Instrumentation、Histograms and summaries。
  • [2] OpenMetrics 规范(_total 后缀与单位约定)。
  • [3] 关联数据模型、RED 埋点、relabeling 基数急救。

本页目录