2. Prometheus 数据模型基础:Metric、Label、Sample、Time Series 的概念与关系

在深入容量规划建模之前,必须牢固掌握 Prometheus 的核心数据模型。该模型是理解长周期数据存储、查询效率以及集群资源消耗的基石。Prometheus 的数据模型可以概括为:一个 Metric 名称加上一组 Label 唯一标识一条 Time Series,而 Time Series 由一系列有序的 Sample 组成。

2.1 四大核心概念定义

概念 英文 定义与说明 容量规划关注点
指标 Metric 被监控对象的量化描述,如 http_requests_total、node_cpu_seconds_total。它定义了“测量什么”。 指标基数(Metric Cardinality)直接影响存储和内存开销。
标签 Label 键值对(Key-Value),用于对 Metric 进行多维度的特征描述,如 method="GET"、status="200"、instance="10.0.0.1:9090"。 标签的基数(Label Cardinality)是爆炸性增长的根源,是容量规划的核心风险点。
样本 Sample 时间序列中某个时间点的具体数值。由两部分组成:时间戳(Timestamp)和值(Value)。 样本的采集频率和大小决定了数据写入吞吐量和磁盘空间占用。
时间序列 Time Series 同一组 Metric + Label 组合下,按时间排序的 Sample 流。它是 Prometheus 存储和查询的基本单元。 时间序列的总数(Active Series)是衡量 Prometheus 实例负载的最关键指标。
SRE 经验提示: 在集群升级前的容量评估中,活跃时间序列数(Active Series)是比 CPU 或内存更敏感的指标。一旦 Series 数量超过阈值,Prometheus 的查询响应会急剧恶化,甚至触发 OOM。务必在升级前通过 prometheus_tsdb_head_series 等指标监控当前水位。

2.2 概念关系与数据模型结构

下图展示了 Metric、Label、Sample 与 Time Series 之间的层级关系。一个 Metric 名称可以衍生出无数条 Time Series,区别仅在于 Label 组合的不同。

Prometheus 数据模型概念关系图 Prometheus 数据模型:Metric → Time Series → Sample Metric: http_requests_total Time Series 集合(由 Label 唯一标识) method="GET", status="200" method="POST", status="201" method="GET", status="500" Sample: (t1, 1024) → (t2, 1056) Sample: (t1, 50) → (t2, 72) Sample: (t1, 3) → (t2, 5) 关键关系: • Metric + Label 组合 = 唯一 Time Series。Label 每增加一个维度,Series 数量可能呈乘积增长。

2.3 数据模型在存储层的映射

Prometheus 的本地存储(TSDB)将上述概念转化为物理存储结构。理解这种映射关系,有助于我们估算磁盘空间和 I/O 压力。

  • Metric 名称:在 TSDB 中被编码为 __name__ 这个特殊的 Label。
  • Label 对:所有 Label(包括 __name__)被排序后,通过哈希映射到一个唯一的 Series ID。
  • Sample:按照时间窗口(Block)组织,每个 Block 内包含该时间窗口内所有 Series 的样本数据。样本数据采用 delta-of-delta 和 XOR 压缩算法,大幅降低存储开销。
容量规划公式(简化版):
单节点磁盘占用 ≈ Active Series × 样本大小 × 采集间隔 × 保留时长 × 压缩比
其中,压缩比 通常在 1.3 ~ 3.0 bytes/sample 之间,取决于数据波动程度。波动越小,压缩比越高。

2.4 高基数(High Cardinality)陷阱

这是 SRE 在容量规划中最常遇到的“隐形杀手”。当 Label 的取值组合过多时,Time Series 数量会爆炸式增长。

场景 Label 示例 Series 数量估算 对 Prometheus 的影响
正常 HTTP 监控 method (4种), status (10种), endpoint (20个) 4 × 10 × 20 = 800 可接受,资源消耗低
引入 user_id method (4), status (10), endpoint (20), user_id (10000) 4 × 10 × 20 × 10000 = 8,000,000 灾难性!TSDB 写入放大,查询超时,OOM 风险极高
容器化环境(Pod IP) container (10), pod (500), namespace (5) 10 × 500 × 5 = 25,000 中等规模,需关注 Pod 重建导致的 Series 抖动
升级前必查项: 使用 topk(10, count by (__name__) ({__name__=~".+"})) 查询当前 Prometheus 中 Series 数量最多的 Top 10 指标。如果发现某个指标拥有数百万 Series,且其 Label 包含 user_id、ip、email 等无限增长的维度,必须在升级前进行 Relabel 丢弃 或 Recording Rule 聚合,否则升级后的查询负载会压垮新实例。

2.5 数据模型与查询语言(PromQL)的关联

PromQL 的向量匹配逻辑直接基于数据模型:

  • Instant Vector:在某个时间点,查询所有 Time Series 的最新 Sample。
  • Range Vector:在某个时间范围内,查询一组 Time Series 的多个 Sample。
  • 聚合操作(如 sum by (method)):先按 Label 分组,再对组内的 Series 进行聚合,这依赖于 Label 的维度。

在容量规划建模中,我们经常使用 rate() 和 increase() 函数处理 Counter 类型指标,这些函数需要读取 Range Vector,即需要从磁盘读取多个 Sample。Series 基数越高,Range 查询的 I/O 放大效应越明显。

# 示例:计算集群升级期间,每秒请求数的变化趋势
# 该查询需要遍历所有 method/status/endpoint 组合的 Series
rate(http_requests_total{job="my-app"}[5m])