Prometheus 基础查询
Prometheus 提供了一个名为 PromQL (Prometheus query language) 的函数式查询语言,它允许用户实时选择和聚合时间序列数据。表达式的结果可以显示为图形,在 Prometheus 的表达式浏览器中作为表格数据查看,或者由外部系统通过 HTTP API 使用。
查询表达式语言数据类型
在 Prometheus 的表达式语言中,表达式或子表达式可以被计算为以下四种类型之一:
- Instant vector 瞬时向量是一组时间序列,包含每个时间序列的单个样本,所有数值共享相同的时间戳
- Range vector 范围向量是一组时间序列,包含每个时间序列随时间变化的一系列数据点
- Scalar 标量是一个简单的数字浮点值
- String 简单的字符串值,目前未使用
根据用例的不同(例如,当图形化和显示表达式的输出时),只有一些类型是合法的,作为用户指定表达式的结果。例如,返回即时向量的表达式是唯一可以直接绘制图形的类型。
类型
字符串类型
字符串可以指定为单引号、双引号或反引号中的文字。
PromQL 遵循与 Go 相同的转义规则。在单引号或双引号中,反斜杠开始转义序列,转义序列后面可能跟着 a, b, f, n, r, t, v或\。可以使用八进制(\nnn)或十六进制(\xnn, \unnnn和\ unnnnnnn)来提供特定的字符。
反引号内不处理转义。与 Go 不同的是,Prometheus 不会丢弃反引号内的换行符。
例子:
"this is a string"
'these are unescaped: \n \\ \t'
`these are not unescaped: \n ' " \t`
浮点类型
标量浮点值可以写成文字整数或浮点数的格式(为了更好的可读性,只包含空格):
[-+]?(
[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
| 0[xX][0-9a-fA-F]+
| [nN][aA][nN]
| [iI][nN][fF]
)
比如:
23
-2.43
3.4e-9
0x8f
-Inf
NaN
时间序列数据选择器
瞬时向量
瞬时向量允许在给定的时间戳中选择一组时间序列和每个时间序列的单个数值:在最简单的形式中,只指定一个指标名称。这将得到一个包含所有时间序列元素的瞬时向量,这些元素具有这个指标名称。
比如 http_requests_total 这个指标名称的时间序列:
http_requests_total
可以通过在花括号({})中添加逗号分隔的标签匹配器列表来进一步筛选这些时间序列。
这次依然选择 http_requests_total 指标名称的时间序列,这些时间序列的 job
标签(label)设置为 prometheus
,group 标签(label)设置为canary
:
http_requests_total{job="prometheus",group="canary"}
在匹配过程中可以使用否定匹配标签值,或者使用正则表达式匹配标签值。存在以下的标签匹配操作符:
- =: 选择与提供的字符串完全相等的标签。
- !=: 选择不等于提供的字符串的标签。
- =~: 选择与提供的字符串正则表达式匹配的标签。
- ~: 选择与提供的字符串不匹配的标签。
例如下列的语句可以查询 http_requests_total 时间序列数据,将环境变量为 staging, testing, development 的数据查询出来 ,并且排除掉 HTTP 方法为 GET 的数据。
http_requests_total{environment=~"staging|testing|development",method!="GET"}
匹配空标签值的标签匹配器也会选择完全没有特定标签集的所有时间序列。正则表达式匹配完全锚定。对于相同的标签名,可以有多个匹配器。
向量查询必须指定与空字符串不匹配的名称或至少一个标签,不能匹配到并且只匹配到空的字符串。比如下面的表达式是错误的:
# 错误的查询
{job=~".*"}
相反,下面这些表达式是有效的,因为它们都有不匹配空标签值的选择器。
# 正确的查询
{job=~".+"}
{job=~".*",method="get"}
通过匹配内部的__name__
标签(双下划线开头的名称都是内部保留的指标名称),标签也可以应用于指标名称。例如,表达式httprequeststotal等价于`{__name="http_requests_total"}`。
=
, !=
, =~
, !~
这些都可以使用。下面的表达式选择所有名称以job开头的指标
{__name__=~"job:.*"}
指标名称不能是bool、on、ignore、group_left和 group_right 这些关键字之一。下面的表达式是错误的:
# 错误的查询
on{}
解决这个限制的方法是使用__name__
标签:
# 正确用法
{__name__="on"}
范围向量
范围向量的工作方式类似于瞬时向量,除了它们从当前瞬间选择返回的数据范围。从语法上讲,在向量选择器的末尾用方括号([])附加一个时间持续时间,以指定每个结果范围向量元素应该获取的时间值。
在这个例子中,我们选择指标名称为 http_requests_total
且 job
label 设置为 prometheus
的所有时间序列在过去5分钟内记录的所有值:
http_requests_total{job="prometheus"}[5m]
时间范围
时间长度指定为一个数字,后面紧跟着下列单位之一:
- ms - milliseconds 毫秒
- s - seconds 秒
- m - minutes 分钟
- h - hours 小时
- d - days 天,假设一天总是 24h。为什么要假设,因为我们现在是作为计量单位来进行描述,如果按照自然地球日或者其他各种历法,一天不一定是完整的,正正好好的 24 h 。
- w - weeks 周,假设一周总是 7 天。同理上一条。
- y - years 年,假设一年总是 365 天。闰年是 366 天嘛,所以这个时间计量单位是不分平年和闰年的。
时间持续范围可以通过连接来组合。单位必须按从大到小的顺序排列。一个给定的单位在一段时间内只能出现一次。 比如
1h
2h20m
5m
15s
offset 偏移量修饰符
offset 偏移量修饰符允许更改查询中单个瞬时向量和范围向量的时间偏移量。
例如,下面的表达式返回 http_requests_total
相对于当前查询计算时间过去5分钟的值:
http_requests_total offset 5m
注意,偏移量修饰符总是需要紧跟在向量后面,比如下面这个例子
# 正确写法
sum(http_requests_total{method="GET"} offset 5m)
# 错误写法
sum(http_requests_total{method="GET"}) offset 5m
对于范围向量也是一样的。比如下面这个例子将返回 http_requests_total
一周前的5分钟速率:
rate(http_requests_total[5m] offset 1w)
为了与时间前移的时间比较,可以指定一个负偏移:
rate(http_requests_total[5m] offset -1w)
这个特性可以通过设置 --enable-feature=promql-negative-offset
标志来启用。
@ 修饰符
@
修饰符允许在查询中更改单个瞬时向量和范围向量的计算时间。提供给@
修饰符的时间是一个unix时间戳,用浮点数值。
比如下面这个 例子就会返回 http_requests_total
在2021-01-04T07:40:00+00:00 的值。
http_requests_total @ 1609746000
注意,偏移量修饰符总是需要紧跟在向量后面,比如下面这个例子
# 正确写法
sum(http_requests_total{method="GET"} @ 1609746000)
# 错误写法
sum(http_requests_total{method="GET"}) @ 1609746000
对于范围向量也是一样的。比如下面这个例子将返回 http_requests_total
指定时间的值:
rate(http_requests_total[5m] @ 1609746000)
@
修饰符在int64范围内支持上述浮点值的所有表示。它还可以与offset
修饰符一起使用,其中offset
是相对于@
修饰符时间应用的,而不管哪个修饰符先写。这两个查询将产生相同的结果。
# offset after @
http_requests_total @ 1609746000 offset 5m
# offset before @
http_requests_total offset 5m @ 1609746000
默认情况下,这个修饰符是禁用的,因为它破坏了 PromQL 不能提前查看示例的评估时间的不变条件。它可以通过设置--enable-feature=promql-at-modifier
标志来启用。
另外,start()
和end()
也可以作为@
修饰符的值。
对于范围查询,它们分别解析到范围查询的开始和结束,并对所有步骤保持相同。
对于即时查询,start()
和end()
都解析为计算时间。
http_requests_total @ start()
rate(http_requests_total[5m] @ end())
子查询
子查询允许您针对给定的范围和分辨率运行即时查询。子查询的结果是一个范围向量。
语法:
操作符
Prometheus 支持许多二进制和聚合操作符。这些在表达式语言操作符页面中有详细描述。
函数
Prometheus 支持几个对数据进行操作的函数。表达式语言函数页面详细描述了这些函数。
注释
PromQL支持以 # 开头的行注释。例子:
# This is a comment
PromQL 查询的缺陷
过期数据
在执行查询语句时,抽样数据所在的时间戳将独立于当前的实际时间序列数据进行选择。这主要是为了支持像聚合(sum、avg等)这样的情况,在这种情况下,多个聚合时间序列不能在时间上精确对齐。由于它们的独立性,Prometheus 需要在这些时间戳上为每个相关的时间序列分配一个值。它只需要在这个时间戳之前获取最新的样本即可。
如果指标获取或规则评估不再返回以前存在的时间序列数据,则该时间序列将被标记为陈旧。如果删除了目标,那么之前返回的时间序列数据很快就会被标记为过时的。
如果在一个时间序列数据被标记为过时之后,在一个采样时间戳上计算查询,则不会为该时间序列返回值。如果随后在该时间序列中摄入了新的数据,它们将被正常退回。
如果在采样时间戳5分钟前没有找到数据(默认情况下),则该时间序列在此时间点上没有返回值。这实际上意味着,当最新收集的样本时间超过5分钟,或者被标记为陈旧时,时间序列就会从图表中“消失”。
在时间序列中包含时间戳的时间序列将不会被标记为过时。在这种情况下,只应用5分钟阈值。
避免慢查询和重载
如果一个查询需要操作大量的数据,把这个查询结果绘制成图形可能会超时,也有可能使Prometheus 服务或者浏览器崩溃。因此,在执行对未知数据的查询时,尽量或者应当首先在 Prometheus 的表达式浏览器的表格视图中开始执行查询过程,也就是先查询一个瞬时值,直到结果集看起来合理为止(最多是数百个,而不是数千个时间序列)。当经过充分地过滤或聚合数据时,达到一个合理的情况时,切换到图形模式进行绘图。如果表达式仍然需要很长时间才能得到结果,那么可以通过相关规则进行预处理数据以后载执行。
这与 Prometheus 的查询语言特别相关,在这种语言中,像 api_http_requests_total 这样的指标名称选择器可以扩展到数千个带有不同标签的时间序列。还要记住,在多个时间序列上聚合的表达式将在服务器上生成负载,即使输出只是少量的时间序列。这类似于在关系数据库中对一列的所有值求和,即使输出值只是一个数字,也是很慢的。