type
status
date
slug
summary
tags
category
icon
password
PromQL
PromQL
是Prometheus监控系统内置的一种查询语言,PromQL
允许你以灵活的方式选择、聚合等其他方式转换和计算时间序列数据,该语言仅用于读取数据。当 Prometheus 从系统和服务收集指标数据时,它会把数据存储在内置的时序数据库(TSDB)中,要对收集到的数据进行任何处理,我们都可以使用 PromQL 从 TSDB 中读取数据,同时可以对所选的数据执行过滤、聚合以及其他转换操作。可以这么说,PromQL是Prometheus所有应用场景的基础,理解和掌握PromQL是Prometheus入门的第一课。PromQL 的执行方式可以分为两种:
Prometheus 内部自动触发执行
:在 Prometheus 服务器中,记录规则和警报规则会定期运行,并执行查询操作来计算规则结果(例如触发报警)。该执行在 Prometheus 服务内部进行,并在配置规则时自动发生。
外部系统或用户通过接口手动触发执行
:外部用户和 UI 界面可以使用 Prometheus 服务提供的 HTTP API 来执行 PromQL 查询。这就是仪表盘软件(例如 Grafana、PromLens 以及 Prometheus 内置 Web UI)访问 PromQL 的方式。数据模型
prometheus中,每个时间序列的聚合数据都由
指标名称(Metric Name)
和标签(Label)
来唯一标识,格式为<metric name>{<label name>=<label value>,···}

指标名称(键)
:通常用于描述系统上要测定的某个特征,反映了被监控样本的含义。如 http_request_total
表示的是对应服务器接收到的HTTP请求总数。
标签(值)
:附加在指标名称之上,从而让指标能够支持多纬度特征;可选项。同一指标可能会适配到多个目标或设备,比如多个主机都有cpu过去一分钟负载,多个主机中的指标名称是一样的,通过使用标签来区分不同主机。例如prometheus_http_requests_total{code=“200”}
和prometheushttp_requests_total{code=“302”}
代表着两个不同的时间序列。用户可以基于这些特征维度过滤,聚合,统计从而产生新的计算后的一条时间序列。
时间序列
:一个带时间戳的数据,这些数据具有一个标识符和一组样本值。随着时间的流式,每一个指标都会以固定的时间间隔生成很多数据采集点。Prometheus通过指标名称以及对应的一组标签唯一定义一条时间序列。指标名称+一组标签=一条时间序列
,一条时间序列下面有很多样本
向量
Prometheus
会将所有采集到的样本数据以时间序列的方式保存在内存数据库中,并且定时保存到硬盘上。时间序列是按照时间戳和值的序列顺序存放的,我们称之为向量(vector),每条时间序列通过指标名称(metrics name)和一组标签集(键值对)命名。在时间序列中的每一个点称为一个样本(sample)。如下所示,可以将时间序列理解为一个以时间为 X 轴的数字矩阵:在时间序列中的每一个点称为一个样本,样本由以下三部分组成:
指标(metric)
:指标名和描述当前样本特征的标签集合
时间戳(timestamp)
:一个精确到毫秒的时间戳
样本值(value)
: 一个 float64 的浮点型数据表示当前样本的值即时向量
在 Prometheus 中,每个 CPU 核心的空闲率可能不同,比如节点上有4颗CPU:CPU0 空闲 10%,CPU1 空闲 20%,CPU2 空闲 50%,CPU3 空闲 20%。如果想计算当前这个服务器的 CPU 平均空闲多少?这就是要聚合计算这个向量中把所有的样本值取出来,求平均值。那我想知道 CPU 的最大占用率,使用最高的是哪一个,空闲最少是哪一个等等。这些计算都是在
即时向量(Instant Vector)
上进行的聚合操作,它可以从多个时间序列中提取当前时刻的样本值并进行计算。所以我可以在一个即时向量上去做一些聚合计算,这就叫即时向量这些指标都是
Prometheus
定期从 metrics
接口那里采集过来的。采集的间隔时间的设置由 prometheus.yml
配置中的 scrape_interval
指定。最多抓取间隔为 30
秒,这意味着至少每 30
秒就会有一个带有新时间戳记录的新数据点,这个值可能会更改,也可能不会更改,但是每隔 scrape_interval
都会产生一个新的数据点。因为数据抓取是在同一时刻进行的,所以在同一时刻都有一个样本,如果为空则表示没有采集到。同一时间采集数据导致prometheus压力过大,所以prometheus会将数据随机散开,所以时间点并不是很精确,但是是同一时间戳上的数据。prometheus数据样本值默认保留一个月。在
TSDB
内部,指标名称也只是一个特殊的标签,标签名为 __name__
,由于这个标签在 PromQL
中随时都会使用,所以在使用 PromQL 查询的时候就被单独写在了标签列表前面了。另外像 method=""
这样的空标签在 Prometheus 种相当于一个不存在的标签,在 Prometheus 代码里面是明确地剥离了空标签的,并不会存储它们。通过指标名查询,比如查询指标prometheus_http_requests_total

在Prometheus内部其实将指标名作为
__name__="指标名"
,查询输入{__name__="prometheus_http_requests_total"}
标签 ,结果同上。因为每个指标都包含__name__
标签,所以输入{__name__=~".+"}
可以查找到所有的序列
区间向量
在 Prometheus PromQL 中,
区间向量(Range Vector)
代表 一段时间范围内的多个数据点,而 即时向量(Instant Vector)
只代表某一时刻的单个数据点。当我们需要分析一段时间内的指标趋势,比如计算过去 5 分钟的 CPU 使用率平均值、最大值或最小值,就需要使用区间向量。区间向量
的语法是在 即时向量
后面加上 时间范围
,例如 [5m]
代表过去5 分钟的所有数据点向量选择器
PromQL
的查询操作可能需要针对若干个时间序列上的样本数据进行,挑选出目标时间序列是构建表达式时最为关键的一步。用户可使用向量选择器表达式来挑选出给定指标名称下的所有时间序列或部分时间序列的即时样本值或至过去某个时间范围内的样本值,前者称为瞬时向量选择器
,后者称为区间向量选择器
。向量选择器由指标名称
和标签选择器
两部分组成:
指标名称:
用于限定特定指标下的时间序列,即负责过滤指标;可选
标签选择器:
用于过滤时间序列上的标签;定义在 {}
之中;可选随时间流逝的样本值被称为向量,瞬时向量在一个序列上只取一个样本值。定义瞬时向量选择器时,以上两个部分应该至少给出一个,因此存在以下三种组合:
- 仅给定指标名称,或在标签名称上使用了空值的标签选择器:返回给定的指标下的所有时间序列各自的即时样本
例如,
prometheus_http_requests_total
和prometheus_http_requests_total{}
的功能相同,都是用于返回这个指标下各时间序列的所有即时样本
- 仅给定标签选择器:返回所有符合给定的标签选择器的所有时间序列上的即时样本
例如,
{code=“200”, job=“prometheus”}
这样的时间序列可能会有着不同的指标名称
- 指标名称和标签选择器的组合:返回给定的指标下的,且符合给定的标签过滤器的所有时间序列上的即时样本
例如,
prometheus_http_requests_total{code=“200”, job=“prometheus”}
用于返回这个指标code
为200
, 并且 job 为 prometheus 的时间序列的即时样本
区间向量选择器的不同之处在于,需要通过在瞬时向量选择器表达式后面添加包含在
[]
里的时长来表达需在时间时序上返回的样本所处的时间范围。时间范围:以当前时间为基准时间点,指向过去一个特定的时间长度,例如[5m]
是指过去5分钟之内。可用的时间单位有 ms(毫秒)
、s(秒)
、m(分钟)
、h(小时)
、d(天)
、w(周)
和 y(年)
。必须使用整数时间,且能够将多个不同级别的单位进行串联组合,以时间单位由大到小为顺序,例如 1h30m,但不能使用 1.5h瞬时查询
直接通过类似于PromQL表达式
http_requests_total
查询时间序列时,返回值中只会包含该时间序列中的最新的一个样本值,这样的返回结果我们称之为瞬时向量
。而相应的这样的表达式称之为瞬时向量表达式。一个瞬时查询有以下参数:PromQL表达式和一个评估的时间戳在 Prometheus 的 WebUI 界面中表格视图中的查询就是瞬时查询,对应API查询接口
/api/v1/query
。如下图 /api/v1/query?query=xxxx&time=xxxx
中的 query 参数就是 PromQL 表达式,time 参数就是评估的时间戳。瞬时查询可以返回任何有效的 PromQL 表达式类型(字符串、标量、即时和范围向量)。
瞬时向量查询的结果就是样本值,样本值(value): 一个 float64 的浮点型数据表示当前样本的值

区间查询
区间查询主要用于图形,在一个指定的时间范围内显示一个 PromQL 表达式,范围查询的工作方式与即时查询完全相同,这些查询在指定时间范围的评估步长中进行评估。当然,这在后台是高度优化的,在这种情况下,Prometheus 实际上并没有运行许多独立的即时查询。
查询不变,web界面点击graph,在 Prometheus 的 WebUI 界面中图形视图中的查询就是区间查询,API 接口
/api/v1/query_range?query=xxx&start=xxxxxx&end=xxxx&step=14
中的 query 参数就是 PromQL 表达式,区间向量的查询结果url包含start开始时间、stop结束时间、step 步长,因为绘图不可能是一个点
在查询的时候可以选择查询过去的数据,比如
指标名称[5m]
表示查询指标序列最近 5m 的数据,访问过去的数据,对于计算一段时间内的比率或平均数等聚合会非常有用。在瞬时向量后加 5m ,这次查询依旧是瞬时查询,查询的结果是区间向量:样本值@时间戳


区间向量是不能绘图的

瞬时向量选择器和区间向量选择器默认都是以当前时间为基准时间,而偏移修饰器用来调整基准时间,使其往前偏移一段时间。偏移修饰器紧跟在选择器后面,使用关键字
offset
来指定要偏移的量。例如:prometheus_http_requests_total offset 5m
表示获取以 prometheus_http_requests_total
为指标名称的所有时间序列在过去 5 分钟之时的即时样本;prometheus_http_requests_total[5m] offset 1d
表示获取距此刻 1 天时间之前的 5 分钟之内的所有样本数据指标类型
抓取目标报告的指标类型:counter、gauge、histogram、summary。PromQL 表达式的结果数据类型:字符串、标量、瞬时向量或区间向量
数据类型 | 说明 |
瞬时向量 | 特定或全部的时间序列集合上,具有相同时间戳的一组样本值。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为瞬时向量表达式。 |
区间向量 | 特定或全部的时间序列集合上,在指定的同一时间范围内的所有样本值,例如[5m]5 分钟 |
标量 | 一个浮点型的数据值 |
字符串 | 一个字符串,支持使用单引号、双引号或反引号进行引用,但反引号中不会对转义字符进行转义 |
指标是用来衡量性能、消耗、效率和许多其他软件属性随时间的变化趋势。它们允许工程师通过警报和仪表盘来监控一系列测量值的演变(如CPU或内存或磁盘使用量、请求持续时间、延迟等)。指标在IT监控领域有着悠久的历史,并被工程师广泛使用,与日志和链路追踪一起被用来检测系统是否有不符合预期的表现。在其最基本的形式中,一个指标数据点是由以下三个部分构成:
一个指标名称
、收集该数据点的时间戳
、一个由数字表示的测量值
从存储上来讲所有的监控指标都是相同的,但是在不同的场景下这些指标又有一些细微的差异。 例如,在
Node Exporter
返回的样本中指标 node_load1
反应的是当前系统的负载状态,随着时间的变化这个指标返回的样本数据是在不断变化的。而指标 node_cpu_seconds_total
所获取到的样本数据却不同,它是一个持续增大的值,因为其反应的是 CPU 的累计使用时间,从理论上讲只要系统不关机,这个值是会一直变大。为了能够帮助用户理解和区分这些不同监控指标之间的差异,Prometheus 定义了 4 种不同的指标类型:Counter(计数器)
、Gauge(仪表盘)
、Histogram(直方图)
、Summary(摘要)

Counter 只增不减的计数器
Counter
类型的指标其工作方式和计数器一样,只增不减,单调递增。可以使用计数器来表示服务请求等等,所以它对于存储诸如服务的 HTTP 请求数量或使用的 CPU 时间之类的信息非常有用。常见的监控指标,如 http_requests_total
、node_cpu_seconds_total
都是 Counter 类型的监控指标。
node_cpu_seconds_total 是一个 Prometheus 的核心指标,它来自于 Node Exporter,用来监控 节点(Node)CPU 的使用情况。这是一个 累计型计数器(counter)指标,单位是秒,表示 某个 CPU 核心在某种状态下累计运行的时间。

node_cpu_seconds_total
这个指标来源于 Linux 系统中的 /proc/stat
文件,表示系统启动以来,CPU 在各个状态下累计消耗的时间(单位是秒)。由于它是 Counter 类型,所以数值只会不断递增。
每个 CPU 状态(如 idle、user、system 等)都会对应一个标签值,比如
mode="idle"
就表示 CPU 空闲的时间。由于原始值是一个不断累加的总量,我们通常并不关心当前总值,而是关注单位时间内的变化速率,也就是一段时间内 CPU 花了多少时间在某个状态上。这时候可以使用 rate() 函数计算增长速率:这个表达式的含义是:计算过去 5 分钟内 CPU 空闲时间的平均增长速率,结果乘以 100,换算为百分比形式,表示 CPU 的空闲率。如果你想看的是 CPU 使用率,可以用
100 - idle
百分比 的方式计算。
可能你会觉得一直增加的数据没什么用处,了解服务从开始有多少请求有什么价值吗?但是需要记住,每个指标都存储了时间戳的,所有你的 HTTP 请求数现在可能是 1000 万,但是 Prometheus 也会记录之前某个时间点的值,我们可以去查询过去一个小时内的请求数,当然更多的时候我们想要看到的是请求数增加或减少的速度有多快,因此通常情况对于 Counter 指标我们都是去查看变化率而不是本身的数字。PromQL 内置的聚合操作和函数可以让用户对这些数据进行进一步的分析,例如,通过 rate() 函数获取 HTTP 请求的增长率:
获取的是当前五分钟的样本数据,样本数据是一个区间,也就是一个时间范围。counter在绘制图形的时候,会使用rate函数去计算一下变化率,增长率
Gauge 可增可减的仪表盘
Gauge
类型的指标侧重于反应系统的当前状态,因此这类指标的样本数据可增可减。常见指标如 node_memory_MemFree_bytes
(当前主机空闲的内存大小)、node_memory_MemAvailable_bytes
(可用内存大小)都是 Gauge 类型的监控指标。由于 Gauge 指标仍然带有时间戳存储,所有我们可以看到随时间变化的值,通常可以直接把它们绘制出来,这样就可以看到值本身而不是变化率了,通过 Gauge 指标,用户可以直接查看系统的当前状态。
这些简单的指标类型都只是为每个样本获取一个数字,但 Prometheus 的强大之处在于如何让你跟踪它们,比如我们绘制了两张图,一个是 HTTP 请求的变化率,另一个是分配的 gauge 类型的实际内存,直接从图上就可以看出这两个之间有一种关联性,当请求出现峰值的时候,内存的使用也会出现峰值,但是我们仔细观察也会发现在峰值过后,内存使用量并没有恢复到峰值前的水平,整体上它在逐渐增加,这表明很可能应用程序中存在内存泄露的问题,通过这些简单的指标就可以帮助我们找到这些可能存在的问题。

通过 PromQL 内置函数
delta()
可以获取样本在一段时间范围内的变化情况。例如,计算 CPU 温度在两个小时内的差异:还可以直接使用
predict_linear()
对数据的变化趋势进行预测。例如,预测系统磁盘空间在 4 个小时之后的剩余情况:这个函数是用来做预测的,predict_linear()
这个函数对于磁盘空间来说的话是非常有用的,因为磁盘在实际使用的过程当中一下子就增长起来了,这是一个缓慢的过程,所以可以根据这个函数来判断4个小时之后磁盘剩余的情况,根据这个情况就可以提前去将磁盘空间清理或者扩容。Histogram 直方图
Prometheus 中的直⽅图指标允许⼀个服务记录⼀系列数值的分布。直⽅图通常⽤于跟踪请求的延迟或响应⼤⼩等指标值,当然理论上它是可以跟踪任何根据某种分布⽽产⽣波动数值的⼤⼩。 Prometheus 直⽅图是在客户端对数据进⾏的采样,它们使⽤的⼀些可配置的(例如延迟) bucket 桶对观察到的值进⾏计数,然后将这些 bucket 作为单独的时间序列暴露出来。
对于Prometheus来说
Histogram
会在一段时间范围内对数据进行采样,通常是请求持续时长或响应大小等,并将其计入可配置的 bucket(存储桶)中 ,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。Prometheus 取值间隔的划分采用的是累积区间间隔机制,即每个 bucket 中的样本均包含了其前面所有 bucket 中的样本,因而也称为累积直方图。在计算过去 5 分钟的请求速率时,如果第 5 分钟只有 3 个请求,第 4 分钟有 36 个请求,那么直接取 5 分钟内的平均值可能会产生误导。平均值容易受到
长尾效应
的影响,无法真实反映系统的状态。例如,一个小池塘的平均水深为 1 米,但某处有一个 2 米深的坑,若只看平均水深,会误以为池塘整体安全,忽略了局部的潜在风险。因此,在监控数据分析中,分位数(Percentile) 比平均值更具参考价值。分位数可以将数据划分为多个区间,并统计不同区间内的数据分布。例如,在网站请求时间监控中,可以将请求时延划分为 1ms-100ms、100ms-1s、1s-10s 等多个区间,然后统计 99% 的请求小于 3s,这样比仅仅计算平均值更能准确反映用户体验。如果计算我和马云的平均工资,这个统计对普通人来说毫无意义,但如果采用分位数分析,就能更真实地描述收入分布情况。Histogram 类型的每个指标有一个基础指标名称 ,它会提供多个时间序列:
_sum :
所有样本值的总和
_count :
总的采样次数,它自身本质上是一个 Counter 类型的指标
_bucket{le="<上边界>"} :
观测桶的上边界,即样本统计区间,表示样本值小于等于上边界的所有样本数量
_bucket{le="+Inf"} :
最大区间(包含所有样本)的样本数量
在 Prometheus 内部,直⽅图被实现为⼀组时间序列,每个序列代表指定桶的计数。例如 10ms 以下的请求数、25ms 以下的请求数、50ms 以下的请求数等。在 Prometheus 中每个 bucket 桶的计数器是累加的,这意味着较⼤值的桶也包括所有低数值的桶的计数。在作为直方图⼀部分的每个时间序列上,相应的桶由特殊的 le 标签表示。le 代表的是小于或等于。下图是⼀个非累积直方图的例⼦:

与上⾯相同的直⽅图在 Prometheus 中的累积直⽅图如下所⽰:

Summary 摘要
Histogram 在客户端仅是简单的桶划分和分桶计数,分位数计算由 Prometheus Server 基于样本数据进行估算,因而其结果未必准确,甚至不合理的 bucket 划分会导致较大的误差。Summary 是一种类似于 Histogram 的指标类型,但它在客户端于一段时间内,默认为 10 分钟。每个采样点进行统计,计算并存储了分位数数值,Server 端直接抓取相应值即可。摘要用于记录某些东西的平均大小,可能是计算所需的时间或处理的文件大小,摘要显示两个相关的信息:count(事件发生的次数)和 sum(所有事件的总大小)。对于每个指标,Summary 以指标名称为前缀,生成如下几个指标序列:
_sum :
统计所有样本值之和
_count :
统计所有样本总数
{quantile="x"} :
统计样本值的分位数分布情况,分位数范围:0 ≤ x ≤ 1
如下图计算摘要指标可以返回次数为 3 和总和 15,也就意味着 3 次计算总共需要 15s 来处理,平均每次计算需要花费 5s。下一个样本的次数为 10,总和为 113,那么平均值为 11.3,因为两组指标都记录有时间戳,所以我们可以使用摘要来构建一个图表,显示平均值的变化率,比如图上的语句表示的是 5 分钟时间段内的平均速率

PMQL表达式
算术运算
Prometheus 的查询语言支持基本的算术运算:加法、减法、乘法、除法、模、幂等。比如将一个数字计算当做一个 PromQL 语句,用于标量与标量之间计算,比如:
(2 + 3 / 6) * 2^2
,图形中返回的是一个值为 10 的标量(scalar)类型的数据。
二元运算同样适用于向量和标量之间,例如我们可以将一个字节数除以两次 1024 来转换为 MiB,如下查询语句,计算可用内存,最后计算的结果就是MiB单位

向量匹配
PromQL 的一个强大功能就是可以让我们在向量与向量之间进行二元运算
一对一
一对一的向量匹配
:一对一向量匹配也有两种情况,就是是否按照所有标签匹配进行计算
匹配所有标签的情况:两个指标 foo 和 bar,分别生成了 3 个序列,foo和bar指标里面标签并不是完全对应的
当我们执行查询语句
foo{} + bar{}
的时候,对于向量左边的每一个元素,操作符都会尝试在右边里面找到一个匹配的元素,匹配是通过比较所有的标签来完成的,没有匹配的元素会被丢弃,我们可以看到其中的 foo{color="green", size="medium"}
与 bar{color="green", size="xlarge"}
两个序列的标签是不匹配的,其余两个序列标签匹配,所以计算结果会抛弃掉不匹配的序列,得到的结果为其余序列的值相加。上面例子中其中不匹配的标签主要是因为第二个 size 标签不一致造成的,那么如果我们在计算的时候忽略掉这个标签可以吗?我们在进行计算的时候可以使用 on 或者 ignoring 修饰符来指定用于匹配的标签进行计算,由于示例中两边的标签都具有 color 标签,所以在进行计算的时候我们可以基于该标签(on (color))或者忽略其他的标签(ignoring (size))进行计算,这样得到的结果就是所以匹配的标签序列相加的结果,要注意结果中的标签也是匹配的标签。

一对多
在多数情况下,
on
或者 ignoring
修饰符有助于是查询返回合理的结果,但通常情况用于计算的两个向量之间并不是一对一的关系,更多的是一对多或者多对一的关系。多对一和一对多两种匹配模式指的是一侧的每一个向量元素可以与多侧的多个元素匹配的情况,在这种情况下,必须使用 group 修饰符:group_left
或者 group_right
来确定哪一个向量具有更高的基数(充当多的角色)。也就是说哪个元素是多的那一边,左侧元素比较多需要添加group left,右侧元素多就是group right,让其明确指定一下,注意是基于标签维度的多少。多对一和一对多两种模式一定是出现在操作符两侧表达式返回的向量标签不一致的情况,因此同样需要使用
ignoring
和 on
修饰符来排除或者限定匹配的标签列表。例如 demo_num_cpus 指标告诉我们每个实例的 CPU 核心数量,只有 instance 和 job 这两个标签维度。例如 demo_api_request_duration_seconds_sum 的数据包含了在 path、method、status 等不同维度上花费的总时间,指标 demo_api_request_duration_seconds_count 包含了上面同维度下的请求总次数。则我们可以用下面的语句来查询过去 5 分钟的平均请求持续时间:(平均的时间/平均的数量),标签集合是一样的,所以可以直接去做运算
从上面可以看出,两个指标sum count对应的标签是完全一致的,所以可以做加减乘除运算。PromQL 会通过相同的标签集自动匹配操作符左边和右边的元素,并将二元运算应用到它们身上。由于上面两个指标的标签集合都是一致的,所有可以得到相同标签集的平均请求延迟结果
阈值
PromQL 通过提供一组过滤的二元运算符(>、<、== 等),允许根据其样本值过滤一组序列,这种过滤最常见的场景就是在报警规则中使用的阈值。比如我们想查找在过去 15 分钟内的
status="500"
错误平均变化大于 20% 的所有 HTTP 路径,我们在 rate 表达式后面添加一个 >0.2 的过滤运算符,这个查询只会将错误率大于 20% 的数据过滤出来
这种过滤方式不仅适用于单个数字,PromQL 还允许你用一组时间序列过滤另一组序列。比较运算符会自动应用于比较左侧和右侧具有相同标签集的序列之间。以下示例是选择所有具有 500 错误率且至少比同一路径的总请求率大 50 倍的路径,不过需要注意的是我们必须忽略匹配中的 status 标签,因为在左边一直有这个标签,而右边没有这个标签。

比如我们还可以计算 demo 演示服务实例在一小时内的预测磁盘使用量,但要过滤只有那些预测磁盘已满的实例。(指标是1对1的关系)
有时你可能想知道比较运算符的结果而不实际删除任何输出系列。要实现这一点,我们可以向运算符添加一个 bool 修饰符来保留所有的序列,但是把输出样本值设置为 1(比较为真)或 0(比较为假)。例如,要简单地显示一组数据中哪些请求率高于或低于 0.2/s,我们可以这样查询:
我们可以看到输入序列的结果为 0 或 1,把数字条件转换为了布尔输出值

正则表达式
Prometheus Expression 是 Prometheus 提供的查询语言,用于查询和操作时间序列数据。它包含丰富的函数和运算符,能够对时间序列数据进行提取、聚合和转换。此外,正则表达式 在 Prometheus 查询中也被广泛用于匹配和过滤时间序列数据。
Prometheus 使用 RE2 作为正则表达式引擎,该库具备高性能和安全性,避免了常见的
回溯爆炸
问题。正则表达式主要用于匹配 指标名称
和 标签值
,从而更灵活地筛选监控数据。在 Prometheus Expression(PromQL) 中,可以使用以下四种符号进行标签匹配:
=
:精确匹配,仅匹配完全相同的值
!=
:不匹配,排除等于某个值的标签
=~
:正则匹配,匹配符合正则表达式的标签值
!~
:正则不匹配,排除符合正则表达式的标签值正则表达式在Prometheus expresiom中的常见用法包括
精确匹配
、前缀匹配
、后缀匹配
和包含匹配
,以下是一些常用的正则表达式语法示例和用法:
精确匹配(=)
:匹配完全相同的标签值,如下示例:该查询仅匹配 label_name 取值严格等于 "label_value" 的数据前缀匹配(^)
:匹配以某个字符串开头的标签值,如下示例:该查询匹配所有 label_name 以 "label_value" 开头的时间序列数据后缀匹配($)
:匹配以某个字符串结尾的标签值,如下示例:该查询匹配所有 label_name 以 "label_value" 结尾的时间序列数据包含匹配(.*)
:匹配包含特定字符串的标签值,如下示例:该查询匹配所有 label_name 包含 "label_value" 的时间序列数据变化率
直接绘制原始的 Counter 类型指标数据意义不大,因为其值会不断累加,反映的是累积总量。我们更关心的是 Counter 指标的变化率,即单位时间内的增量,这样可以更直观地了解系统的运行状态和趋势。如下图查询语句可以看到都是不断增长的

rate
rate函数用于计算在
指定时间范围
内计数器每秒增加量的平均值,例如我们要计算 prometheus_http_requests_total
在最近五分钟内的每秒平均变化率,则可以使用下面的查询语句:可以得到如下所示的图形:

进行 rate 计算的时候是选择指定时间范围内的第一个样本和最后一个样本进行计算,假设一分钟内有6个样本数据点,当前时间的样本值减去一分钟之前的样本值,也就是166-100的差值除以60的结果值为平均变化率。
rate结果=时间区间前后两个点的差/时间范围

往往我们需要的是绘制一个图形,那么就需要进行区间查询,指定一个时间范围内进行多次计算,将结果串联起来形成一个图形:

当被抓取指标进程重启时,Counter 指标可能会重置为 0,但
rate()
函数会自动处理这个问题,它会假设 Counter 指标的值只要是减少了就认为是被重置了,然后它可以调整后续的样本,例如,如果时间序列的值为[5,10,4,6]
,则将其视为[5,10,14,16]

注意:
rate()
函数需要在指定窗口下至少有两个样本才能计算输出。一般来说,比较好的做法是选择范围窗口大小至少是抓取间隔的4倍,也就是至少需要4个样本数据点,这样即使在遇到窗口对齐或抓取故障时也有可以使用的样本进行计算,例如,对于 1 分钟的抓取间隔,你可以使用 4 分钟的 Rate 计算,但是通常将其四舍五入为 5 分钟。所以如果使用 query_range
区间查询(dashboard graph界面查询),例如在绘图中,那么范围应该至少是步长的大小,否则会丢失一些数据。另外我们需要注意当把
rate()
函数与一个聚合运算符(例如sum()
函数)结合使用时,需要先rate()
在聚合。否则,当你的目标重新启动时,rate()
函数无法检测到 Counter 的重置。irate
由于使用 rate 或者 increase 函数去计算样本的平均增长速率,容易陷入
长尾问题
。其无法反应在时间窗口内样本数据的突发变化。例如,对于主机而言在 2 分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致 CPU 占用 100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。为了解决该问题,PromQL 提供了另外一个灵敏度更高的函数irate(v range-vector)
。irate 同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate
函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的长尾问题,并且体现出更好的灵敏度,通过 irate
函数绘制的图标能够更好的反应样本数据的瞬时变化状态。那既然是使用最后两个点计算,那为什么还要指定类似于 [1m]
的时间范围呢?这个 [1m]
不是用来计算的,irate
在计算的时候会最多向前在 [1m]
范围内找点,如果超过 [1m]
没有找到数据点,这个点的计算就放弃了。比如计算irate一分钟内变化率,采集一分钟内的样本值,但是只选取最后两位数据进行差值运算,也就是166-138
差值除以60的结果值为平均变化率。irate结果=时间区间内最后两个样本点的差/最后两个样本点的时间差

由于 rate 函数提供了更平滑的结果,因此在长期趋势分析或者告警中更推荐使用 rate 函数,因为当速率只出现一个短暂的峰值时,不应该触发该报警。
increase
使用 increase 函数可以查询指定时间范围内的总增量,它基本上相当于速率乘以时间范围选择器中的秒数

increase 函数表达式的结果和使用 rate 函数计算的结果整体图形趋势都是一样的,只是 Y 轴的数据不一样而已。increase 函数表示数量,rate 函数表示百分比。
deriv
rate、irate 和 increase 函数只能输出非负值的结果,对于跟踪一个可以上升或下降的值的指标(如温度、内存或磁盘空间),可以使用 delta 和 deriv 函数来代替。deriv 函数可以计算一个区间向量中各个时间序列二阶导数,使用简单线性回归,
deriv(v range-vector)
的参数是一个区间向量,返回一个瞬时向量,这个函数一般只用在 Gauge 类型的时间序列上。例如,计算在 15 分钟的窗口下,磁盘根空闲空间的变化速率,可以帮助监测磁盘空间是否在增长或减少

predict_linear
predict_linear 函数可以预测一个 Gauge 类型的指标在未来指定一段时间内的值,例如我们可以根据过去 15 分钟的变化情况,来预测根分区 (/) 的磁盘空闲空间在未来 1 小时(3600 秒)后的值,可以帮助预警磁盘即将填满的情况

聚合
Prometheus 的时间序列数据采用多维度模型,支持通过标签灵活区分数据来源。在实际监控中,单个时间序列的价值有限,我们更关注的是对同类数据在不同维度下的聚合分析。这种聚合操作,允许我们对某一类指标进行求和、计数、平均、分位数、标准差、方差等统计处理,从而获得更具业务意义的结果。
需要特别注意的是,当将 rate() 与聚合操作(如 sum())或时间聚合函数(如以 _over_time 结尾的函数)组合使用时,必须先使用 rate(),再进行聚合。这是因为 Prometheus 中的 Counter 类型指标是单调递增的,一旦目标重启,数值会重置为 0。rate() 函数具备识别并修复这种重置的能力,而 sum() 等聚合函数则不会处理这种情况。所以先去用
rate()
进行计算,再使用 sum()
去聚合。聚合表达式
PromQL中的聚合操作语法格式可采用如下面两种格式之一:
聚合函数
Prometheus 的聚合操作由聚合函数针对一组值进行计算并返回值作为结果。Prometheus 内置提供的 11 个聚合函数,也称为聚合运算符:
sum()
:对样本值求和
min()
:求取样本值中的最小者
max()
:求取样本值中的最大者
avg()
:对样本值求平均值
count()
:对分组内的时间序列进行数量统计
stddev()
:对样本值求标准差,以帮助用户了解数据的波动大小(或称之为波动程度)
stdvar()
:对样本值求方差,它是求取标准差过程中的中间状态
topk()
:逆序返回分组内的样本值最大的前 k 个时间序列及其值,即最大的 k 个样本值
bottomk()
:顺序返回分组内的样本值最小的前 k 个时间序列及其值,即最小的 k 个样本值
quantile()
:分位数,用于评估数据的分布状态,该函数会返回分组内指定的分位数的值,即数值落在小于等于指定的分位区间的比例
count_values()
:对分组内的时间序列的样本值进行数量统计,即等于某值的样本个数分组聚合
对查询结果事先按照某种分类机制进行分组(group by),并将查询结果按组进行聚合计算,也是较为常见的需求。例如分组统计、分组求平均值、分组求和等。分组聚合:先分组、后聚合
without
:从结果向量中删除由without子句指定的标签,未指定的那部分标签则用作分组标准;
by
: 仅使用by子句中指定的标签进行聚合,结果向量中出现但未被 by 指定的标签则会被忽略;例如,我们想知道的 apiserver 服务 5m 处理的请求数,那么可以所有单个的速率相加就可以,这样就可以算得 5m 内所有的请求数量

可以看到绘制出来的图形没有保留任何标签维度,标签为空。一般来说可能我们希望保留一些维度,例如,我们可能更希望计算指定标签的变化率,这个时候我们可以在 sum() 聚合器中添加一个by() 修饰符来保留需要的标签,by()类似于SQL语句里面的group by,这里是根据什么去做分组聚合

without函数是标签取反,表示不包含这些标签

时间聚合
有时候我们可能想在每个序列中按时间进行聚合。例如,使尖锐的曲线更平滑,或深入了解一个序列在一段时间内的最大值。为了基于时间来计算这些聚合,PromQL 提供了一些与标签聚合运算符类似的函数,但是在这些函数名前面附加了
_over_time()
,语法格式如下:下面的函数列表允许传入一个区间向量,它们会聚合每个时间序列的范围,并返回一个瞬时向量:
avg_over_time(range-vector)
:区间向量内每个指标的平均值。
min_over_time(range-vector)
:区间向量内每个指标的最小值。
max_over_time(range-vector)
:区间向量内每个指标的最大值。
sum_over_time(range-vector)
:区间向量内每个指标的求和。
count_over_time(range-vector)
:区间向量内每个指标的样本数据个数。
quantile_over_time(scalar, range-vector)
:区间向量内每个指标的样本数据值分位数。
stddev_over_time(range-vector)
:区间向量内每个指标的总体标准差。
stdvar_over_time(range-vector)
:区间向量内每个指标的总体标准方差。例如,我们查询 demo 实例中使用的 goroutine 的原始数量,可以使用查询语句 go_goroutines{job="demo"},这会产生一些尖锐的峰值图:

我们可以通过对图中的每一个点来计算 10 分钟内的 goroutines 数量进行平均来使图形更加平滑:他要算十分钟之内的平均值,其实就是将这些值全部加起来求平均。
排序
如果我们想要对查询的结果进行排序,或者只选择一组序列中最大或最小的值。可以使用
sort()
升序 或者 sort_desc()
降序函数来实现对输出结果进行排序。例如,计算每个实例的 CPU 使用率,并从高到低进行排序,我们可以用下面的语句进行查询:有时候我们对所有的时间序列并不是很感兴趣,只对最大或最小的几个序列感兴趣。我们可以使用
topk()
和 bottomk()
这两个运算符来操作,可以返回 K 个最大或最小的序列,比如只显示前 3 个 CPU 使用率最高的实例,我们可以使用下面的语句来查询:逻辑运算
and示例:当多个条件被同时满足时进行显示
or示例:当可用空间大于200000或者小于2500000都会显示
unless示例:当第一个值的标签和第二个值的标签不匹配的情况下会输出
检测
每次 Prometheus 成功抓取一个目标(Target)时,都会自动生成一个名为 up 的指标样本,用来表示这个目标的抓取状态。如果抓取成功
up=1
,如果抓取失败,目标不可达或响应异常up=0

如果只希望显示down掉的实例,可以通过过滤0来获取
获取down掉的实例总数,一般情况下这种类型的查询会用于健康状态告警
在实际使用中,仅仅依赖指标的样本值,比如
up == 0
并不总是足够的。因为 Prometheus 中的时间序列是有数据才存在的,如果某个目标(Target)从未被 Prometheus 成功抓取过,那么它压根不会出现在 up 指标中,也就查询不到。换句话说,up 这个指标只能反映已知目标的状态,不能发现缺失的目标。
如果 Prometheus 从来没有抓取过某个目标服务,那么对应的 up 指标根本不会存在。这种情况下,直接使用
up
指标进行判断就会出问题:查询的结果可能是空的,根本看不到任何关于这个目标的状态信息。这时我们就需要使用 absent()
函数来判断是否存在某个时间序列。absent()
的作用是:当输入的指标序列不存在时,它返回一个值为 1 的样本;如果该指标存在,则什么也不返回。 它非常适合用来检测本该出现但却缺失的指标。例如,我们执行absent(up{job="node-exporter"})
将得到一个空的输出,如果测试一个没有被抓取的job是否存在的时候,将得到样本值1
如果被抓取的指标存在,则查询结果返回空

子查询
子查询通常会套用多层查询语句,消耗资源比较多,目前用的不算太多
avg_over_time()
:指定间隔内所有点的平均值
示例:查询一天空闲空间的平均值:avg_over_time(node_filesystem_files_free[1d])
min_over_time():指定间隔中所有点的最小值。
示例:一天空闲空间的最大值:
max_over_time(node_filesystem_files_free[1d])
max_over_time():指定间隔内所有点的最大值。
sum_over_time():指定时间间隔内所有值的总和。
- 链接:www.qianshuai.cn/article/pmql
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。