4. 指标数据模型:Metric、Label、Timestamp、Value 四要素
聊到监控数据采集,很多人第一反应是「怎么把数据拿过来」。但我觉得,真正决定一个监控系统能走多远的,是数据模型怎么设计。
说白了,数据模型就是监控系统的「骨架」。骨架歪了,后面再怎么填肉也长不成人样。我见过太多项目,前期随便搞个 key-value 存着,等规模一上来,查询慢、聚合难、存储爆炸,最后只能推倒重来。
今天咱们就聊聊监控数据模型的四个核心要素:Metric、Label、Timestamp、Value。这四个东西,你搞明白了,监控系统就稳了一半。
4.1 四要素拆解:一个都不能少
先看一个最典型的监控数据点长什么样:
http_requests_total{method="GET", endpoint="/api/users", status="200"} 1024 1700000000
这行数据里,四个要素全齐了:
- Metric(指标名):
http_requests_total,表示「HTTP 请求总数」 - Label(标签):
{method="GET", endpoint="/api/users", status="200"},描述这个指标的维度 - Value(值):
1024,当前这个时间点的具体数值 - Timestamp(时间戳):
1700000000,Unix 时间戳,精确到秒
嗯,这里要注意:时间戳不是可选项。我见过有人把时间戳省了,想着「反正采集端自己打时间」。结果呢?网络延迟、采集端时钟漂移,数据全乱套了。时间戳必须由数据源自己带,采集端只做透传。
4.2 Metric 命名:别让名字成为你的坑
指标名怎么起?我个人习惯遵循 Prometheus 的命名规范:
- 命名空间:用
namespace_开头,比如nginx_、mysql_、app_ - 层级结构:用下划线分隔,比如
app_requests_total、app_requests_duration_seconds - 单位后缀:带上单位,比如
_seconds、_bytes、_total - 不要用驼峰:全小写 + 下划线,避免大小写敏感带来的混乱
最佳实践示例:
# 好的命名
app_http_requests_total
app_http_request_duration_seconds
db_query_latency_milliseconds
# 坏的命名
AppHttpRequestsTotal # 驼峰,容易搞混
http_requests # 没有命名空间,冲突风险高
latency # 没有单位,谁知道是秒还是毫秒?
我在项目中遇到过一件事:两个团队各自开发微服务,一个用 request_count,一个用 requests_total。结果聚合查询时,同一个指标名对应了两种语义,排查了整整两天。从那以后,我强制要求所有指标名必须带命名空间前缀。
4.3 Label 设计:维度爆炸是最大的敌人
Label 是监控数据模型的灵魂。没有 Label,你只能看到「总请求数」,有了 Label,你才能看到「每个接口的请求数」「每个状态码的分布」。
但 Label 也是一把双刃剑。你想想看,每个 Label 组合都会产生一个新的时间序列。如果有 10 个 Label,每个 Label 有 10 个取值,那就是 10^10 = 100 亿条序列。存储直接爆炸。
我曾经踩过的坑:
给每个 HTTP 请求都加了一个 request_id 标签,想着方便追踪。结果一天产生了 2 亿条时间序列,存储集群直接 OOM。后来才明白:高基数(High Cardinality)标签是监控系统的头号杀手。
Label 设计的原则,我总结为三条:
- 低基数优先:Label 的取值数量控制在 100 以内。比如
status_code(几十种)、method(几种)、region(几十个) - 不要放唯一标识:
user_id、request_id、ip这些高基数标签,请放到日志里,别放监控里 - 标签值标准化:统一大小写、去空格、去特殊字符。比如
status="200"不要写成status="200 OK"
小技巧:
如果你不确定某个标签是否合适,问自己一个问题:「这个标签的取值会不会超过 1000 种?」如果会,就别放。
4.4 Timestamp 与 Value:精度和范围的选择
时间戳和值看起来简单,但细节不少。
| 要素 | 推荐格式 | 注意事项 |
|---|---|---|
| Timestamp | Unix 秒级时间戳 | 不要用毫秒或纳秒,除非你的场景需要亚秒级精度。秒级时间戳兼容性最好,存储也省空间 |
| Value | float64 | 大部分监控系统用 64 位浮点数。注意:不要存字符串,不要存布尔值,统一转成数值 |
为什么推荐秒级?我做过一个对比测试:同样 1 亿条数据,秒级时间戳占 8 字节,毫秒级占 12 字节。看起来不多,但乘以 1 亿,就是 400MB 的差距。而且大部分监控场景(比如 10 秒采集一次),秒级精度完全够用。
4.5 数据模型设计最佳实践总结
说了这么多,我画了一张图,把核心逻辑串起来:
这张图把四要素和最佳实践串在了一起。你设计数据模型时,就照着这个框架来,基本不会出大问题。
4.6 一个完整的例子
最后,我拿一个真实场景来演示:监控一个电商应用的订单接口。
# 指标定义
app_order_created_total{status="success", region="cn-beijing", env="prod"} 1523 1700000000
app_order_created_total{status="failed", region="cn-beijing", env="prod"} 12 1700000000
app_order_created_total{status="success", region="cn-shanghai", env="prod"} 987 1700000000
# 指标名:app_order_created_total
# 命名空间:app_
# 业务域:order_
# 指标名:created_total(累计值)
# 标签:
# status:success / failed(低基数,2种取值)
# region:cn-beijing / cn-shanghai(低基数,几十种)
# env:prod / staging / dev(低基数,几种)
# 时间戳:1700000000(秒级)
# 值:1523(float64)
这个设计,你想想看,是不是既清晰又高效?查询时,可以按 region 聚合看总订单量,也可以按 status 看成功率,还能按 env 区分环境。而且每个标签的基数都很低,不会导致序列爆炸。
避坑指南:
我曾经在一个项目中,把 user_id 作为标签存了进去。结果用户量从 1 万涨到 100 万,时间序列从 10 万条涨到 1000 万条,查询响应时间从 100ms 涨到 30 秒。最后不得不重建数据,把 user_id 从标签里移除,改用日志系统存储。
数据模型设计,说白了就是「取舍」的艺术。你不可能什么维度都存,也不可能什么查询都支持。抓住 Metric、Label、Timestamp、Value 这四个要素,把基数和精度控制好,你的监控系统就能跑得稳、查得快、存得省。