云原生技术系列-监控系统Prometheus之列三:PromQL

背景

PromQL(Prometheus Query Language)是 Prometheus 自己开发的表达式语言,语言表现力很丰富,内置函数也很多。使用它可以对时序数据进行筛选和聚合。

主要内容

  • PromQL 语法
  • PromQL 操作符
  • PromQL 函数

正文

PromQL 语法

数据类型

PromQL 表达式计算出来的值有以下几种类型:

  • 瞬时向量 (Instant vector): 一组时序,每个时序只有一个采样值
  • 区间向量 (Range vector): 一组时序,每个时序包含一段时间内的多个采样值
  • 标量数据 (Scalar): 一个浮点数
  • 字符串 (String): 一个字符串,暂时未用

时序选择器

瞬时向量选择器

瞬时向量选择器用来选择一组时序在某个采样点的采样值。

最简单的情况就是指定一个度量指标,选择出所有属于该度量指标的时序的当前采样值。比如下面的表达式:

http_requests_total

可以通过在后面添加用大括号包围起来的一组标签键值对来对时序进行过滤。比如下面的表达式筛选出了 job 为 prometheus,并且 group 为 canary 的时序:

http_requests_total{job="prometheus", group="canary"}

匹配标签值时可以是等于,也可以使用正则表达式。总共有下面几种匹配操作符:

  • =:完全相等
  • !=: 不相等
  • =~: 正则表达式匹配
  • !~: 正则表达式不匹配

下面的表达式筛选出了 environment 为 staging 或 testing 或 development,并且 method 不是 GET 的时序:

http_requests_total{environment=~"staging|testing|development",method!="GET"}

度量指标名可以使用内部标签 name 来匹配,表达式 http_requests_total 也可以写成 {name="http_requests_total"}。表达式 {name=~"job:.*"} 匹配所有度量指标名称以 job: 打头的时序。

区间向量选择器

区间向量选择器类似于瞬时向量选择器,不同的是它选择的是过去一段时间的采样值。可以通过在瞬时向量选择器后面添加包含在 [] 里的时长来得到区间向量选择器。比如下面的表达式选出了所有度量指标为 http_requests_total 且 job 为 prometheus 的时序在过去 5 分钟的采样值。

http_requests_total{job="prometheus"}[5m]

时长的单位可以是下面几种之一:

  • s:seconds
  • m:minutes
  • h:hours
  • d:days
  • w:weeks
  • y:years

偏移修饰器

前面介绍的选择器默认都是以当前时间为基准时间,偏移修饰器用来调整基准时间,使其往前偏移一段时间。偏移修饰器紧跟在选择器后面,使用 offset 来指定要偏移的量。比如下面的表达式选择度量名称为 http_requests_total 的所有时序在 5 分钟前的采样值。

http_requests_total offset 5m

下面的表达式选择 http_requests_total 度量指标在 1 周前的这个时间点过去 5 分钟的采样值。

http_requests_total[5m] offset 1w

PromQL 操作符

二元操作符

PromQL 的二元操作符支持基本的逻辑和算术运算,包含算术类、比较类和逻辑类三大类。

算术类二元操作符

算术类二元操作符有以下几种:

  • +:加
  • -:减
  • *:乘
  • /:除
  • %:求余
  • ^:乘方

算术类二元操作符可以使用在标量与标量、向量与标量,以及向量与向量之间。

二元操作符上下文里的向量特指瞬时向量,不包括区间向量。

标量与标量之间,结果很明显,跟通常的算术运算一致。
向量与标量之间,相当于把标量跟向量里的每一个标量进行运算,这些计算结果组成了一个新的向量。
向量与向量之间,会稍微麻烦一些。运算的时候首先会为左边向量里的每一个元素在右边向量里去寻找一个匹配元素(匹配规则后面会讲),然后对这两个匹配元素执行计算,这样每对匹配元素的计算结果组成了一个新的向量。如果没有找到匹配元素,则该元素丢弃。

比较类二元操作符

比较类二元操作符有以下几种:

  • == (equal)
  • != (not-equal)
  • > (greater-than)
  • < (less-than)
  • >= (greater-or-equal)
  • <= (less-or-equal)

比较类二元操作符同样可以使用在标量与标量、向量与标量,以及向量与向量之间。默认执行的是过滤,也就是保留值。可以通过在运算符后面跟 bool 修饰符来使得返回值 0 和 1,而不是过滤。

  • 标量与标量之间,必须跟 bool 修饰符,因此结果只可能是 0(false) 或 1(true)。
  • 向量与标量之间,相当于把向量里的每一个标量跟标量进行比较,结果为真则保留,否则丢弃。如果后面跟了 bool 修饰符,则结果分别为 1 和 0。
  • 向量与向量之间,运算过程类似于算术类操作符,只不过如果比较结果为真则保留左边的值(包括度量指标和标签这些属性),否则丢弃,没找到匹配也是丢弃。如果后面跟了 bool 修饰符,则保留和丢弃时结果相应为 1 和 0。

逻辑类二元操作符

逻辑操作符仅用于向量与向量之间。

  • and:交集
  • or:合集
  • unless:补集
    具体运算规则如下:
  • vector1 and vector2 的结果由在 vector2 里有匹配(标签键值对组合相同)元素的 vector1 里的元素组成。
  • vector1 or vector2 的结果由所有 vector1 里的元素加上在 vector1 里没有匹配(标签键值对组合相同)元素的 vector2 里的元素组成。
  • vector1 unless vector2 的结果由在 vector2 里没有匹配(标签键值对组合相同)元素的 vector1 里的元素组成。

二元操作符优先级

PromQL 的各类二元操作符运算优先级如下:

  1. ^
  2. *, /, %
  3. +, -
  4. ==, !=, <=, <, >=, >
  5. and, unless
  6. or

向量匹配

前面算术类和比较类操作符都需要在向量之间进行匹配。共有两种匹配类型,one-to-one 和 many-to-one / one-to-many。

One-to-one 向量匹配

这种匹配模式下,两边向量里的元素如果其标签键值对组合相同则为匹配,并且只会有一个匹配元素。可以使用 ignoring 关键词来忽略不参与匹配的标签,或者使用 on 关键词来指定要参与匹配的标签。语法如下:

<vector expr> <bin-op> ignoring(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) <vector expr>

比如对于下面的输入:

method_code:http_errors:rate5m{method="get", code="500"}  24
method_code:http_errors:rate5m{method="get", code="404"}  30
method_code:http_errors:rate5m{method="put", code="501"}  3
method_code:http_errors:rate5m{method="post", code="500"} 6
method_code:http_errors:rate5m{method="post", code="404"} 21

method:http_requests:rate5m{method="get"}  600
method:http_requests:rate5m{method="del"}  34
method:http_requests:rate5m{method="post"} 120

执行下面的查询:

method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m

得到的结果为:

{method="get"}  0.04            //  24 / 600
{method="post"} 0.05            //   6 / 120

也就是每一种 method 里 code 为 500 的请求数占总数的百分比。由于 method 为 put 和 del 的没有匹配元素所以没有出现在结果里。

Many-to-one / one-to-many 向量匹配

这种匹配模式下,某一边会有多个元素跟另一边的元素匹配。这时就需要使用 group_left 或 group_right 组修饰符来指明哪边匹配元素较多,左边多则用 group_left,右边多则用 group_right。其语法如下:

<vector expr> <bin-op> ignoring(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> ignoring(<label list>) group_right(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr>

组修饰符只适用于算术类和比较类操作符。

对于前面的输入,执行下面的查询:

method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m

将得到下面的结果:

{method="get", code="500"}  0.04            //  24 / 600
{method="get", code="404"}  0.05            //  30 / 600
{method="post", code="500"} 0.05            //   6 / 120
{method="post", code="404"} 0.175           //  21 / 120

也就是每种 method 的每种 code 错误次数占每种 method 请求数的比例。这里匹配的时候 ignoring 了 code,才使得两边可以形成 Many-to-one 形式的匹配。由于左边多,所以需要使用 group_left 来指明。

Many-to-one / one-to-many 过于高级和复杂,要尽量避免使用。很多时候通过 ignoring 就可以解决问题。

聚合操作符

PromQL 的聚合操作符用来将向量里的元素聚合得更少。总共有下面这些聚合操作符:

  • sum:求和
  • min:最小值
  • max:最大值
  • avg:平均值
  • stddev:标准差
  • stdvar:方差
  • count:元素个数
  • count_values:等于某值的元素个数
  • bottomk:最小的 k 个元素
  • topk:最大的 k 个元素
  • quantile:分位数

聚合操作符语法如下:

<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]

其中 without 用来指定不需要保留的标签(也就是这些标签的多个值会被聚合),而 by 正好相反,用来指定需要保留的标签(也就是按这些标签来聚合)。

下面来看几个示例:

sum(http_requests_total) without (instance)

http_requests_total 度量指标带有 application、instance 和 group 三个标签。上面的表达式会得到每个 application 的每个 group 在所有 instance 上的请求总数。效果等同于下面的表达式:

sum(http_requests_total) by (application, group)

下面的表达式可以得到所有 application 的所有 group 的所有 instance 的请求总数。

sum(http_requests_total)

PromQL 函数

Prometheus 内置了一些函数来辅助计算,下面介绍一些典型的,完整的列表请参考 官方文档

  • abs():绝对值
  • sqrt():平方根
  • exp():指数计算
  • ln():自然对数
  • ceil():向上取整
  • floor():向下取整
  • round():四舍五入取整
  • delta():计算区间向量里每一个时序第一个和最后一个的差值
  • sort():排序

参考资料

  1. Prometheus 查询语言
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容