# promQL ## 表达式语言数据类型 在Prometheus的表达式中,表达式或子表达式包括以下四种类型: - 瞬时向量/及时向量(instant vector) , 一组时间序列,每个时间序列包含单个样本,他们共享相同的时间戳。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为瞬时向量表达式 - 区间向量(Range vector),一组时间序列,每个时间序列包含一段时间范围内的样本数据。 - 标量(Scalar),一个浮点型的数据值 - 字符串(String),一个简单的字符串 瞬时向量表达式返回的数据类型是唯一可以直接绘制成图表的数据类型 ## 字面量 ### 字符串 字符串可以用单引号、双引号和反引号指定为文字常量 PromQL 遵循[与 Go 相同的转义规则](https://golang.org/ref/spec#String_literals)。在单引号或双引号中,用反斜杠来表示转义序列,后面可以跟 `a`, `b`, `f`, `n`, `r`, `t`, `v` 或 `\`。特殊字符可以使用八进制(`\nnn`)或者十六进制(`\xnn`,`\unnnn` 和 `\Unnnnnnnn`)。 与 Go 不同,Prometheus 不会对反引号内的换行符进行转义。 ### 标量 标量浮点值可以字面上写成 `[-](digits)[.(digits)]` 的形式。例如:-2.43 ## 时间序列过滤器 ### 瞬时向量过滤器 瞬时向量过滤器允许在指定的时间戳内选择一组时间序列和每个时间序列的单个样本值。在最简单的形式中,指定指标(metric)名称。这将生成包含此指标名称的所有时间序列的元素的瞬时向量。 例如:选择指标名称为 `apiserver_request_total` 的所有时间序列: ``` apiserver_request_total ``` 可以通过向花括号(`{}`)里附加一组标签来进一步过滤时间序列 例如:选择指标名称为`apiserver_request_total`,`code`的标签值为`200`,`client`为`OpenAPI-Generator/11.0.0/python`的时间序列: ``` apiserver_request_total{code="200",client="OpenAPI-Generator/11.0.0/python"} ``` PromQL还支持用户根据时间序列的标签匹配模式来对时间序列进行过滤,目前主要支持两种匹配模式,完全匹配和正则匹配。共有以下几种标签匹配运算符 - `=`:选择与提供字符串完全相同的标签。 - `!=`:选择与提供字符串不相同的标签 - `=~`:选择正则表达式与提供的字符串(或子字符串)相匹配的标签 - `!~`:选择正则表达式与提供的字符串(或子字符串)不匹配的标签。 例如:选择指标名称为`apiserver_request_total`,`client`为`OpenAPI-Generator/11.0.0/python`或`Prometheus/2.23.0`,`verb`不等于`LIST`的时间序列 ``` apiserver_request_total{client=~"OpenAPI-Generator/11.0.0/python|Prometheus/2.23.0",verb!="LIST"} ``` **如果没有指定标签的标签过滤器会选择该指标名称的所有时间序列** 所有的promQL表达式必须至少包含一个指标名称,或者一个不会匹配到空字符串的标签过滤器 > 非法表达式 ``` {job=~".*"} ``` > 合法表达式 ``` {job=~".+"} {jon=~".*",method="get"} ``` 除了使用`{label=value}`的形式以外,还可以使用内置的`__name__` 标签来指定监控指标名称。例如:表达式`apiserver_request_total`等效于`{__name__="apiserver_request_total"}`。也可以使用除`=`之外的过滤器`(!= 、=~ 、~)`,例如以下表达式选择指标名称以`job:`开头的所有指标 ``` {__name__ =~ "job:.*"} ``` ### 区间向量过滤器 区间向量与瞬时向量的工作方式类似,唯一的差异在于在区间向量表达式中我们需要定义时间选择的范围,时间范围通过时间范围选择器 `[]` 进行定义,以指定应为每个返回的区间向量样本值中提取多长的时间范围。 时间范围通过数字来表示,单位可以使用以下其中之一的时间单位: - `s` - 秒 - `m` - 分钟 - `h` - 小时 - `d` - 天 - `w` - 周 - `y` - 年 例如:选择在过去 5 分钟内指标名称为 `apiserver_request_total`,`client`标签值为 `Prometheus/2.23.0` 的所有时间序列: ``` apiserver_request_total{client="Prometheus/2.23.0"}[5m] ``` ### 时间位移操作 在瞬时向量表达式或区间向量表达式中,都是以当前时间为基准: ``` apiserver_request_total #瞬时向量表达式,选择当前最新的数据 apiserver_request_total{}[5m] #区间向量表达式,选择以当前时间为基准,5分钟以内的数据 ``` 而如果想要查询,5分钟前的瞬时样本数据,或昨天一天的区间内的样本数据?就可以使用位移操作,关键字`offset` 例如,以下表达式返回相当于过去5分钟的`apiserver_request_total`的值 ``` apiserver_request_total offset 5m ``` 注意:`offset` 关键字需要紧跟在选择器(`{}`)后面,正确写法 ``` sum(apiserver_request_total{client="Prometheus/2.23.0"} offset 5m) ``` 错误写法 ``` sum(apiserver_request_total{client="Prometheus/2.23.0"}) offset 5m ``` 此操作同样适用于区间向量。以下表达式返回指标`apiserver_request_total`一周前的5分钟之内HTTP请求的增长率 ``` rate(apiserver_request_total[5m] offset 1w) ``` ## 操作符 ### 二元算术符 Prometheus 的查询语言支持基本的逻辑运算和算术运算。对于两个瞬时向量, [匹配行为](https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching)可以被改变。 #### 算术二元运算符 在 Prometheus 系统中支持下面的二元算术运算符: - `+` 加法 - `-` 减法 - `*` 乘法 - `/` 除法 - `%` 模 - `^` 幂等 二元运算操作符支持 `scalar/scalar(标量/标量)`、`vector/scalar(向量/标量)`、和 `vector/vector(向量/向量)` 之间的操作。 在两个标量之间进行数学运算,得到的结果也是标量。 在向量和标量之间,这个运算符会作用于这个向量的每个样本值上。例如:如果一个时间序列瞬时向量除以 2,操作结果也是一个新的瞬时向量,且度量指标名称不变, 它是原度量指标瞬时向量的每个样本值除以 2。 如果是瞬时向量与瞬时向量之间进行数学运算时,过程会相对复杂一点,运算符会依次找到与左边向量元素匹配(**标签完全一致**)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。同时新的时间序列将不会包含指标名称。 例如,如果我们想根据 `node_disk_written_bytes_total ` 和 `node_disk_read_bytes_total` 获取主机磁盘IO的总量,可以使用如下表达式: ``` node_disk_written_bytes_total + node_disk_read_bytes_total ``` #### 布尔运算符 目前,Prometheus 支持以下布尔运算符: - `==` (相等) - `!=` (不相等) - `>` (大于) - `<` (小于) - `>=` (大于等于) - `<=` (小于等于) 布尔运算符被应用于 `scalar/scalar(标量/标量)`、`vector/scalar(向量/标量)`,和`vector/vector(向量/向量)`。默认情况下布尔运算符只会根据时间序列中样本的值,对时间序列进行过滤。我们可以通过在运算符后面使用 `bool` 修饰符来改变布尔运算的默认行为。使用 bool 修改符后,布尔运算不会对时间序列进行过滤,而是直接依次瞬时向量中的各个样本数据与标量的比较结果 `0` 或者 `1`。 在两个标量之间进行布尔运算,必须提供 bool 修饰符,得到的结果也是标量,即 `0`(`false`)或 `1`(`true`)。例如: ``` 2 > bool 1 #结果为1 ``` 瞬时向量和标量之间的布尔运算,这个运算符会应用到某个当前时刻的每个时序数据上,如果一个时序数据的样本值与这个标量比较的结果是 `false`,则这个时序数据被丢弃掉,如果是 `true`, 则这个时序数据被保留在结果中。如果提供了 bool 修饰符,那么比较结果是 `0` 的时序数据被丢弃掉,而比较结果是 `1` 的时序数据被保留。例如: ``` apiserver_request_count > 1000 #瞬时向量 > 1000的保存在结果集中 apiserver_request_count > bool 1000 #所有都保存在结果集中,大于1000的结果显示为1,否则显示为0 ``` 瞬时向量与瞬时向量直接进行布尔运算时,同样遵循默认的匹配模式:**依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行相应的操作,如果没找到匹配元素,或者计算结果为 false,则直接丢弃。如果匹配上了,则将左边向量的度量指标和标签的样本数据写入瞬时向量。**如果提供了 bool 修饰符,那么比较结果是 `0` 的时序数据被丢弃掉,而比较结果是 `1` 的时序数据(只保留左边向量)被保留。 #### 集合运算符 使用瞬时向量表达式能够获取到一个包含多个时间序列的集合,我们称为瞬时向量。 通过集合运算,可以在两个瞬时向量与瞬时向量之间进行相应的集合操作。目前,Prometheus 支持以下集合运算符: - `and` (并且) - `or` (或者) - `unless` (排除) **vector1 and vector2** 会产生一个由 `vector1` 的元素组成的新的向量。该向量包含 vector1 中完全匹配 `vector2` 中的元素组成。 **vector1 or vector2** 会产生一个新的向量,该向量包含 `vector1` 中所有的样本数据,以及 `vector2` 中没有与 `vector1` 匹配到的样本数据。 **vector1 unless vector2** 会产生一个新的向量,新向量中的元素由 `vector1` 中没有与 `vector2` 匹配的元素组成。 ### 匹配模式 向量与向量之间进行运算操作时会基于默认的匹配规则:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。 接下来将介绍在 PromQL 中有两种典型的匹配模式:一对一(one-to-one),多对一(many-to-one)或一对多(one-to-many)。 #### 一对一匹配 一对一匹配模式会从操作符两边表达式获取的瞬时向量依次比较并找到唯一匹配(标签完全一致)的样本值。默认情况下,使用表达式: ```bash vector1 vector2 ``` 在操作符两边表达式标签不一致的情况下,**可以使用`on(label list)` 或者 `ignoring(label list)`来修改便签的匹配行为。使用 `ignoreing` 可以在匹配时忽略某些便签。而 `on` 则用于将匹配行为限定在某些便签之内.** ``` ignoring(