【Prometheus·入门篇】数据模型:Metric 类型、标签与时间序列

前言

Prometheus 的核心是时间序列数据。理解它的数据模型------指标类型、标签、时间序列------是写好 PromQL 和做好监控的前提。本篇深入讲解数据模型的每个细节。


一、时间序列(Time Series)

什么是时间序列

复制代码
一个时间序列 = 指标名 + 标签组合 + 一系列带时间戳的值

http_requests_total{method="GET", status="200", handler="/api"} 
  → [(t1, 100), (t2, 120), (t3, 135), (t4, 140), ...]

其中:
  - 指标名: http_requests_total
  - 标签: {method="GET", status="200", handler="/api"}
  - 数据点: (时间戳, 值)

时间序列的标识

复制代码
时间序列唯一标识 = 指标名 + 所有标签的键值对

http_requests_total{method="GET", status="200"}    ← 序列 A
http_requests_total{method="GET", status="500"}    ← 序列 B
http_requests_total{method="POST", status="200"}   ← 序列 C

每个唯一的标签组合就是一个独立的时间序列。标签值的组合数量称为基数(cardinality)。

基数问题

复制代码
✅ 低基数(推荐):
  http_requests_total{method="GET", status="200"}     → ~10 个序列
  http_requests_total{method="POST", status="500"}    → 
  http_requests_total{method="DELETE", status="404"}  →

❌ 高基数(禁止):
  http_requests_total{user_id="user_12345", request_id="req_abc123"}  
  → 每个用户×每个请求 = 新序列
  → 100万用户 = 100万序列 → TSDB 爆炸

⚠️ 踩坑提示:高基数是 Prometheus 性能的头号杀手。永远不要用 user_id、email、IP 地址等高维值作为标签。


二、四种指标类型

1. Counter(计数器)

特征 :只增不减(除非重启归零),适合用 rate() 计算速率。

复制代码
http_requests_total
  时间 t1: 100
  时间 t2: 120  (+20)
  时间 t3: 135  (+15)
  时间 t4: 140  (+5)

典型场景:请求总数、错误总数、处理的字节数。

PromQL 用法

promql 复制代码
# 错误:直接用 Counter 值没意义
http_requests_total    # 只是当前累计值

# 正确:用 rate() 计算每秒速率
rate(http_requests_total[5m])    # 过去 5 分钟平均每秒请求数

# 用 increase() 计算总量增长
increase(http_requests_total[1h])  # 过去 1 小时增加了多少请求

应用埋点示例

python 复制代码
from prometheus_client import Counter

http_requests_total = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

# 使用
http_requests_total.labels(
    method='GET',
    endpoint='/api/users',
    status='200'
).inc()

2. Gauge(仪表盘)

特征:可增可减,反映当前状态。

复制代码
memory_usage_bytes
  时间 t1: 2147483648 (2GB)
  时间 t2: 2254857830 (2.1GB)
  时间 t3: 1879048192 (1.75GB)  ← 可以下降
  时间 t4: 2013265920 (1.875GB)

典型场景:内存使用量、CPU 使用率、队列长度、连接数、温度。

PromQL 用法

promql 复制代码
# 直接查询当前值
memory_usage_bytes

# 计算变化率
deriv(memory_usage_bytes[5m])    # 每秒变化量

# 预测
predict_linear(memory_usage_bytes[1h], 4*3600)  # 4小时后预测值

# 超过阈值
memory_usage_bytes > 8e+09   # 大于 8GB

应用埋点示例

python 复制代码
from prometheus_client import Gauge

queue_size = Gauge(
    'message_queue_size',
    'Current message queue size',
    ['queue_name']
)

# 使用
queue_size.labels(queue_name='orders').set(42)
queue_size.labels(queue_name='orders').inc()    # +1
queue_size.labels(queue_name='orders').dec()    # -1

3. Histogram(直方图)

特征:将数据分桶统计,适合计算分位数。

复制代码
http_request_duration_seconds
  Bucket le="0.005":  10   # ≤5ms 的请求 10 个
  Bucket le="0.01":   50   # ≤10ms 的请求 50 个(包含上面 10 个)
  Bucket le="0.025":  200  # ≤25ms 的请求 200 个
  Bucket le="0.05":   350
  Bucket le="0.1":    450
  Bucket le="0.25":   490
  Bucket le="0.5":    498
  Bucket le="1":      499
  Bucket le="2.5":    499
  Bucket le="+Inf":   500  # 所有请求
  Sum: 45.2          # 总耗时
  Count: 500         # 总请求数

典型场景:请求延迟、响应大小。

PromQL 用法

promql 复制代码
# 计算 P99 延迟
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# 计算 P50(中位数)
histogram_quantile(0.50, rate(http_request_duration_seconds_bucket[5m]))

# 平均延迟
rate(http_request_duration_seconds_sum[5m]) / rate(http_request_duration_seconds_count[5m])

应用埋点示例

python 复制代码
from prometheus_client import Histogram

request_duration = Histogram(
    'http_request_duration_seconds',
    'HTTP request duration',
    ['method', 'endpoint'],
    buckets=(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0)
)

# 使用
with request_duration.labels(method='GET', endpoint='/api/users').time():
    # 处理请求
    handle_request()

4. Summary(摘要)

特征:客户端直接计算分位数,不需要聚合。

复制代码
http_request_duration_seconds
  Quantile 0.5: 0.012   # 中位数 12ms
  Quantile 0.9: 0.045   # P90 45ms
  Quantile 0.99: 0.150  # P99 150ms
  Sum: 45.2
  Count: 500

典型场景:单实例延迟监控(不跨实例聚合)。

Summary vs Histogram 对比

特性 Histogram Summary
分位数计算 服务端(PromQL) 客户端
可聚合 ✅ 支持跨实例聚合 ❌ 不可聚合
灵活性 高(任意分位数) 低(预定义分位数)
客户端开销 高(需维护流式分位数)
推荐场景 生产环境 特殊单实例场景

⚠️ 踩坑提示:Summary 的分位数不能跨实例聚合。如果你有 3 个实例,每个的 P99 都是 100ms,合并后的 P99 不是 100ms。要用 Histogram。


三、标签(Labels)深入

标签的作用

复制代码
没有标签:
  http_requests_total = 1000
  → 只知道"总共 1000 个请求"

有标签:
  http_requests_total{method="GET", status="200", service="user-service"} = 800
  http_requests_total{method="POST", status="201", service="user-service"} = 150
  http_requests_total{method="GET", status="500", service="user-service"} = 50
  → 知道每个方法、每个状态码、每个服务各多少请求

标签命名规范

python 复制代码
# ✅ 好的标签
labels={
    'method': 'GET',          # HTTP 方法
    'status': '200',          # HTTP 状态码
    'service': 'user-service',# 服务名
    'env': 'production'       # 环境
}

# ❌ 差的标签
labels={
    'user_id': '12345',       # 高基数!
    'email': 'a@b.com',      # 高基数!
    'request_id': 'abc123',  # 高基数!
    'timestamp': '1234567',  # 不要在标签里放时间戳
}

命名约定

复制代码
指标名:
  - 小写下划线:http_requests_total(不是 HTTP_Requests_Total)
  - 后缀表示类型:_total(counter)、_bytes(gauge)、_seconds(histogram)
  - 单位后缀:_bytes、_seconds、_count

标签名:
  - 小写下划线:method(不是 Method)
  - 简洁明确:status(不是 http_status_code)

四、指标命名约定

官方建议格式

复制代码
<application>_<subsystem>_<name>_<unit>_<type>

示例:
  http_requests_total           # HTTP 请求总数
  node_memory_MemTotal_bytes    # Node Exporter 内存总量
  http_request_duration_seconds # HTTP 请求延迟(秒)
  container_cpu_usage_seconds_total  # 容器 CPU 使用

常见命名模式

类型 命名模式 示例
Counter *_total http_requests_total
Gauge *_current, *_usage memory_usage_bytes
Histogram *_duration_seconds, *_size_bytes request_duration_seconds
Summary *_duration_seconds rpc_duration_seconds

五、TSDB 存储原理

倒排索引

复制代码
时间序列:
  S1: http_requests_total{method="GET", status="200"}
  S2: http_requests_total{method="GET", status="500"}
  S3: http_requests_total{method="POST", status="200"}

倒排索引:
  __name__="http_requests_total" → [S1, S2, S3]
  method="GET" → [S1, S2]
  method="POST" → [S3]
  status="200" → [S1, S3]
  status="500" → [S2]

查询 http_requests_total{method="GET"}:
  交集(method="GET", __name__="...") → [S1, S2]

数据压缩

复制代码
时间戳压缩(Delta-of-Delta):
  t1=1000, t2=1005, t3=1010, t4=1015
  delta: 5, 5, 5
  delta-of-delta: 0, 0, 0 → 压缩为 3 bits

值压缩(Gorilla XOR):
  v1=1.0, v2=1.1, v3=1.2
  XOR: 0.1 → 压缩为 ~4 bits

存储容量估算

复制代码
假设 100 万时间序列,15s 采集间隔,保存 15 天:

数据点数 = 1,000,000 × (15×24×3600/15) = 1,000,000 × 86400 = 86.4B
  → 每个 ~1.37 bytes → ~118 GB

但 Prometheus 压缩后实际约:
  ~1.5 bytes/sample → ~130 GB → 仍然偏大

优化方案:
  - 减少时间序列数(控制基数)
  - 降低采集频率(非关键指标 30s/60s)
  - 缩短保存时间(7-15 天)
  - 使用 Thanos 长期存储

六、指标设计最佳实践

1. 控制基数

python 复制代码
# ❌ 每个用户一个序列
Counter('orders_total', ['user_id'])  # 100万用户 = 100万序列

# ✅ 按状态码分组
Counter('orders_total', ['status', 'payment_method'])  # ~20 序列

2. 使用有意义的标签

python 复制代码
# ❌ 不清晰的标签
Histogram('latency', ['v1', 'v2'])

# ✅ 清晰的标签
Histogram('http_request_duration_seconds', ['method', 'route', 'status'])

3. 避免重复指标

python 复制代码
# ❌ 重复
Counter('http_requests_total')
Counter('http_requests_count')  # 同一概念两个指标

# ✅ 一个指标
Counter('http_requests_total')

4. 合理设置 Histogram 桶

python 复制代码
# ❌ 默认桶不适合
Histogram('db_query_duration_seconds', buckets=(0.005, 0.01, ..., 10.0))
# 如果 DB 查询都在 1-50ms,大部分桶没用

# ✅ 定制桶
Histogram('db_query_duration_seconds', buckets=(0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0))

要点回顾

概念 要点
时间序列 指标名 + 标签组合 = 唯一序列
Counter 只增不减,用 rate/increase
Gauge 可增可减,直接用值
Histogram 分桶统计,服务端算分位数,可聚合
Summary 客户端算分位数,不可聚合
标签基数 控制在合理范围,避免高维标签
命名规范 小写下划线,后缀表示类型和单位
TSDB 倒排索引 + Gorilla 压缩

下一篇预告

理解了数据模型后,下一篇 【Prometheus·入门篇】PromQL 查询:语法、聚合运算与常用模式 将深入 PromQL 查询语言,带你掌握监控查询的核心技能。

相关推荐
heimeiyingwang10 小时前
【Prometheus·部署篇】配置详解:scrape_config、relabeling 与服务发现
prometheus
heimeiyingwang4 天前
【Prometheus·入门篇】Prometheus 是什么:从 Nagios 到云原生监控的演进
prometheus
秋风点枝5 天前
第一篇:认识 Prometheus —— 云原生监控的基础
云原生·prometheus
名字还没想好☜5 天前
Prometheus 告警实战:写 alerting rules、用 Alertmanager 做路由分组与抑制
运维·前端·javascript·docker·kubernetes·prometheus
SamChan906 天前
用Prometheus+Grafana搭建PDF翻译服务监控看板:指标采集与告警实战
python·ai·pdf·grafana·prometheus·机器翻译
^酸酸6 天前
Prometheus 监控架构部署实战:二进制与 Docker 双方案
docker·架构·prometheus
ylj_dev6 天前
从 0 构建 AI Workload Platform(七):可观测性、故障注入与性能验证
go·prometheus·可观测性·opentelemetry·故障注入
蒲锘6 天前
Devops项目阶段 10:Prometheus 监控与 Alertmanager 邮件告警
prometheus
.柒宇.9 天前
运维常见面试题_07_配置管理与监控(Ansible · Prometheus · Alertmanager)· 容器编排(Kubernetes)
运维·k8s·ansible·prometheus