深入理解 PromQL

Prometheus 監控系統是雲原生環境下主流的監控系統,在各大廠都有比較廣泛的應用。

Prometheus 開創性地提出了 PromQL 查詢語法,大大簡化了監控面板的配置門檻,使得應用開發者可以自由地配置、組合監控面板。

PromQL 功能非常強大,大部分應用開發者只需要瞭解最簡單的函數(如 rate、delta、histogram_quantile)就可以實現絕大部分需求。另一方面,這也導致很多人對 PromQL 並沒有很深入的理解,無法掌握一些高級查詢功能,遇到一些報錯的時候不明所以。

比如:

1、案例 1:offset 位置不對報錯,必須在 selector 或者 subquery 後面

2、案例 2:ranges只能接在 vector selectors 後面

本文試圖闡述 PromQL 的組成部分,幫助大家深入的理解 PromQL 語句的含義,以及能夠根據所想寫出合適的 PromQL 語句。

PromQL 解析

四則混合運算,可以拆分成 數字、操作符和括號,掌握了運算規則,再長的算式都變得很好理解。PromQL 也是如此。

Prometheus 的指標(Metric)包括 Counter、Gauge、Histogram、Summary 四種基本類型,部分 PromQL 的函數確實也有要求指定的類型,但這裏的細節不在本文的討論範圍內。

PromQL 主要包含以下幾個組成部分(下列組成部分的劃分是我個人根據自身的經驗和理解做出的,如有不同意見歡迎探討)

一個 PromQL 表達式,即是由上述各部分組成的,理解了每個部分的含義,複雜的 PromQL 就很好理解了。更進一步也可以按自己的心意寫出複雜的 PromQL 語句。

本文並不致力於詳盡的講解每一個組成部分,只想澄清最關鍵的一些概念。一些組成部分的細節(如具體的函數)可以去查閱官方文檔。以上各個部分其實在官方文檔上都有提及,但是散落在不同的頁面,不是很好理解其中的關係。

Vectors

Vector 是什麼

Vector 直接翻譯是向量的意思,PromQL 中的 Vector 也可以理解爲向量。

以一個時間序列(TimeSeries)爲例

counter{a="b"c="d"}

等價於

{a="b"c="d"__name__="counter"}

Instant Vector && Range Vector

剛剛講的 dimension 可以理解爲 向量的方向,向量還有一個元素就是向量值,在 Prometheus 中,向量值都是浮點型的數字。

在一個時刻有一個向量值的,就叫做 instant vector

在一個時刻,不僅包含當前時刻的值,還包含前向一段時間範圍的 向量值(確切的說是時刻=>值的鍵值對)的,就叫做 range vector

對於 instant vector,加上時間維度後,可以很容易地畫出圖像,橫座標是時間戳,縱座標是向量值。

對於 range vector,每一個時刻,都包含一組鍵值對信息,例如

以 example_metric{job="C"} 爲例,可以看到 1@1(value@timestamp) 這個點,出現在了上述所有時刻的 ran ge 裏。

在做運算的時候,可能會用到部分或全部這些信息。例如

Vector 擴展——相關注意點

上述兩點,是想說明 Prometheus 適合用於趨勢類監控,並不能做到十分精確。在某一項指標的具體一小段時間,尤其是 irate 這樣的函數結果並不能精確的反應真實情況。不要用 Prometheus 做時間靈敏度、精確度高的監控手段。

另外,基於採樣的監控,採樣間隔期間出現的瞬時峯值也是無法監控到的。

Selectors

Selector 是什麼

Selector——選擇器——是一個基於標籤匹配來獲取符合條件的 timeseries 的 PromQL 對象

Selector 可以定義一組 label 及其對應的匹配規則,一共有四種:

標籤的匹配和存儲是基於倒排索引來實現的。

指標名稱也是標籤,是一個特殊的標籤__name__

Prometheus 的正則匹配是對 label 值的完全匹配,不支持部分匹配。正則匹配支持的規則也有一定限制(與 go 官方的 regex 庫支持範圍一致),如無法支持 “look behind” 等。

Selector 和 Vector 的關係

相同點

不同點

SubQuery

SubQuery 本質上是 range vector,是 instant vector 附加上 [range:resolution] 之後得到的。

在 PromQL 支持的函數中,如果涉及到 instant vector 和 range vector 之間的轉換,幾乎部分都是從 range vector 轉換到 instant vector。這也不難理解,因爲 range vector 所包含的信息更多,這些函數,本質上都是接受較多信息作爲入參,計算出一維的結果進行返回。

例如 irate 函數,可以計算一個 counter 指標的變化率

irate(counter{a="b"c="d"}[5m])

如果我想計算近 5 分鐘變化率的最大值,該怎麼辦?這裏就用到了 SubQuery

max_over_time(irate(counter{a="b"c="d"}[5m])[5m:1m])

irate 返回的結果是一個 instant vector,拼接上 [range:resolution] 之後,就成爲了一個 subquery,也就是一個 range vector,可以被用於 max_over_time 函數的入參。

爲什麼 resolution 是必須的

根據官方文檔, SubQuery 的標準定義爲

Syntax: <instant_query> '[' <range> ':' [<resolution>] ']' [ @ <float_literal> ] [ offset <duration> ]

<resolution> is optional. Default is the global evaluation interval.

官方文檔這裏描述的有點坑,比如既然 resolution 是可選的,爲什麼還會報下面的錯誤?

答案是 雖然 resolution 是可選的,但是冒號不能丟,可以寫成這樣:

max_over_time(irate(vector(5)[5m:1m])[5m:])

此時,默認的 resolution,等於 prometheus 配置中的 global evaluation interval,也就是來自官網配置文檔中的下面這項配置:

爲什麼 SubQuery 中 resolution 是必要的?按照我的理解,這是因爲很多函數計算嚴格依賴 resolution。例如

sum_over_time(vector(5)[5m:1m]) ==25
sum_over_time(vector(5)[5m:2m]) ==10

resolution 在上面的表達式裏,直接決定了 range 內有幾個點參與計算。

與之對比,在 selector 層面,range query selector 中的時間範圍可以不加 resolution。這是因爲對於一個具體的 timeseries,其自身天然包含 resolution 信息(等於指標採集時的間隔)。但是對於 SubQuery,必須需要一個 resolution,來確定 range 內的點的粒度和個數。

各組件關係轉換圖

總結

PromQL 本質上是針對一系列 vector 的操作:selector 是 TimeSeries 轉換爲 Vector 的橋樑,查出來的結果是 full-dimensional vector。然後,再通過一系列函數、操作符,針對 vector 的粒度,來進行運算。

本文試圖將 PromQL 解析爲基本的組成部分,並對其中的關鍵點、易混淆的概念進行了解析。略去的 Operators、Functions 等,讀者可以自行去官網查看詳細的使用說明。相信有了本文作爲基礎,理解官方文檔也會更加快速、更加透徹。

作者介紹

蘭孟然,某大廠資深研發工程師,擅長高性能分佈式服務端開發,對 prometheus 有深入理解,當前從事 presto、spark 引擎的開發 & 維護工作。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/M0WFAQtoCd1aEZHjxTyjMw