子查询支持

2019年1月28日作者 Ganesh Vernekar

简介

顾名思义,子查询是查询的一部分,它允许你在一个查询中进行范围查询,而这在以前是不可能的。这是一个长期存在的特性请求:prometheus/prometheus/1227 

用于支持子查询的pull request 最近已合并到Prometheus中,并将包含在Prometheus 2.7中。下面让我们来详细了解一下。

动机

有时,你会遇到这样的情况,你想用一个较低的解析度/范围(例如 5m)的 rate 来发现问题,同时又想将这些数据聚合到更高的范围(例如,计算 1hmax_over_time)。

以前,上述情况在一个 PromQL 查询中是不可能实现的。如果你想为告警规则或绘图进行范围选择,就需要基于该查询创建一个录制规则,然后在录制规则创建的指标上执行范围选择。例如:max_over_time(rate(my_counter_total[5m])[1h])

当你想快速获得跨越几天或几周的数据结果时,可能需要等待录制规则有足够的数据才能使用。忘记添加录制规则会令人沮丧。为查询的每一步都创建一个录制规则会非常繁琐。

有了子查询的支持,所有等待和沮丧都将不复存在。

子查询

子查询类似于一个 /api/v1/query_range API 调用,但嵌入在一个即时查询中。子查询的结果是一个范围向量。

Prometheus团队在2018年在慕尼黑举行的Prometheus开发者峰会上就子查询的语法达成了一致。以下是关于子查询支持的峰会笔记 ,以及一份简短的用于实现子查询支持的语法设计文档 

<instant_query> '[' <range> ':' [ <resolution> ] ']' [ offset <duration> ]
  • <instant_query> 等同于 /query_range API 中的 query 字段。
  • <range>offset <duration> 类似于范围选择器。
  • <resolution> 是可选的,等同于 /query_range API 中的 step

当未指定解析度时,全局评估间隔将作为子查询的默认解析度。此外,子查询的步长独立对齐,不依赖于父查询的评估时间。

示例

min_over_time 函数内的子查询在1分钟的解析度下,返回过去30分钟内 http_requests_total 指标的5分钟费率。这等同于使用 query=rate(http_requests_total[5m]), end=<now>, start=<now>-30m, step=1m 进行 /query_range API 调用,并取所有接收值的最小值。

min_over_time( rate(http_requests_total[5m])[30m:1m] )

解析

  • rate(http_requests_total[5m])[30m:1m] 是子查询,其中 rate(http_requests_total[5m]) 是要执行的查询。
  • rate(http_requests_total[5m])start=<now>-30mend=<now> 执行,解析度为 1m。请注意,start 时间会独立地与 1m 的步长对齐(对齐的步长为 0m 1m 2m 3m ...)。
  • 最后,所有上述评估的结果都传递给 min_over_time()

下面是一个嵌套子查询的例子,以及默认解析度的使用。最内层的子查询获取一段时间范围内 distance_covered_meters_total 的费率。我们用它来获取费率的 deriv(),同样是在一段时间内。最后取所有导数的最大值。请注意,最内层子查询的 <now> 时间是相对于外层 deriv() 子查询的评估时间而言的。

max_over_time( deriv( rate(distance_covered_meters_total[1m])[5m:1m] )[10m:] )

在大多数情况下,你会需要默认的评估间隔,这是规则默认评估的间隔。自定义解析度在你想计算得更频繁或不那么频繁的情况下会很有用,例如,计算成本高昂的查询,你可能希望计算的频率较低。

结语

虽然子查询可以方便地替代录制规则,但过度使用它们会带来性能问题。复杂的子查询最终应为了效率而转换为录制规则。

也不建议在录制规则中使用子查询。如果你确实需要在录制规则中使用子查询,请创建更多的录制规则。