跳到主要内容

数据模型

Prometheus 采集的监控数据都是以指标(metric)的形式存储在内置的 TSDB 数据库中,这些数据都是时间序列:一个带时间戳的数据,这些数据具有一个标识符和一组样本值。除了存储的时间序列,Prometheus 还可以根据查询请求产生临时的、衍生的时间序列作为返回结果。

时间序列

Prometheus 会将所有采集到的样本数据以时间序列的形式保存在内存数据库中,并定时刷新到硬盘上,时间序列是按照时间戳和值的序列方式存放的,我们可以称之为向量(vector),每一条时间序列都由一个指标名称和一组标签(键值对)来唯一标识。

  • 指标名称反映了被监控样本的含义(如 http_request_total 表示的是对应服务器处理的 HTTP 请求总数)。
  • 标签可以用来区分不同的维度(比如 method="GET"method="POST" 就可以用来区分这两种不同的 HTTP 请求指标数据)。

如下所示,可以将时间序列理解为一个以时间为 Y 轴的数字矩阵:

  ^
. . . . . . . . . . . . . . . . . . . http_request_total{method="GET",status="200"}
. . . . . . . . . . . . . . . . . . . http_request_total{method="POST",status="500"}
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
v
<------------------ 时间 ---------------->

需要注意的是指标名称只能由 ASCII 字符、数字、下划线以及冒号组成,同时必须匹配正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]*(冒号不能用来定义指标名称,是用来表示用户自定义的记录规则)。标签的名称只能由 ASCII 字符、数字以及下划线组成并满足正则表达式 [a-zA-Z_][a-zA-Z0-9_]*,其中以 __ 作为前缀的标签,是系统保留的关键字,只能在系统内部使用,标签的值则可以包含任何 Unicode 编码的字符。

样本

时间序列中的每一个点就称为一个样本(sample),样本由以下 3 个部分组成:

  • 指标:指标名称和描述当前样本特征的标签集
  • 时间戳:精确到毫秒的时间戳数
  • 样本值:一个 64 位浮点数

指标

想要暴露 Prometheus 指标服务只需要暴露一个 HTTP 端点,并提供 Prometheus 基于文本格式的指标数据即可。这种指标格式是非常友好的,基本上的格式看起来类似于下面的这段代码:

# HELP http_requests_total The total number of processed HTTP requests.
# TYPE http_requests_total counter
http_requests_total{status="200"} 8556
http_requests_total{status="404"} 20
http_requests_total{status="500"} 68

其中 # 开头的行是注释信息,用来描述下面提供的指标含义,其他未注释行代表一个样本(带有指标名、标签和样本值),使其非常容易从系统和服务中暴露指标出来。

事实上所有的指标也都是通过如下所示的格式来标识的:

<metric name>{<label name>=<label value>, ...}

例如,指标名称是 http_request_total,标签集为 method="POST", endpoint="/messages",那么我们可以用下面的方式来标识这个指标:

http_request_total{method="POST", endpoint="/messages"}

而事实上 Prometheus 的底层实现中指标名称实际上是以 __name__=<metric name> 的形式保存在数据库中的,所以上面的指标也等同与下面的指标:

{__name__="http_request_total", method="POST", endpoint="/messages"}

所以也可以认为一个指标就是一个标签集,只是这个标签集里面一定包含一个 __name__ 的标签来定义这个指标的名称。

存储格式

Prometheus 按照两个小时为一个时间窗口,将两小时内产生的数据存储在一个块(Block)中,每个块都是一个单独的目录,里面包含该时间窗口内的所有样本数据(chunks),元数据文件(meta.json)以及索引文件(index)。

其中索引文件会将指标名称和标签索引到样本数据的时间序列中,如果该期间通过 API 删除时间序列,删除记录会保存在单独的逻辑文件 tombstone 当中。

当前样本数据所在的块会被直接保存在内存数据库中,不会持久化到磁盘中,为了确保 Prometheus 发生崩溃或重启时能够恢复数据,Prometheus 启动时会通过预写日志(write-ahead-log(WAL))来重新播放记录,从而恢复数据,预写日志文件保存在 wal 目录中,wal 文件包括还没有被压缩的原始数据,所以比常规的块文件大得多。

Prometheus 保存块数据的目录结构如下所示:

.
├── 01FB9HHY61KAN6BRDYPTXDX9YF
│   ├── chunks
│   │   └── 000001
│   ├── index
│   ├── meta.json
│   └── tombstones
├── 01FB9Q76Z0J10WJZX3PYQYJ96R
│   ├── chunks
│   │   └── 000001
│   ├── index
│   ├── meta.json
│   └── tombstones
├── chunks_head
│   ├── 000014
│   └── 000015
├── lock
├── queries.active
└── wal
├── 00000011
├── 00000012
├── 00000013
├── 00000014
└── checkpoint.00000010
└── 00000000

7 directories, 17 files