深入理解 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 主要包含以下幾個組成部分(下列組成部分的劃分是我個人根據自身的經驗和理解做出的,如有不同意見歡迎探討)
-
Scalar(標量)
-
Vectors
-
Instant Vector
-
Range Vector
-
Selectors
-
Instant Query Selector
-
Range Query Selector
-
Operators
-
Arithmetic (算數操作符)
-
Trigonometric(三角函數操作符)
-
Comparison(比較操作符)
-
Logical(邏輯操作符)
-
Binary Operators
-
Aggregation Operators
-
Functions
-
SubQuery
-
Modifiers
一個 PromQL 表達式,即是由上述各部分組成的,理解了每個部分的含義,複雜的 PromQL 就很好理解了。更進一步也可以按自己的心意寫出複雜的 PromQL 語句。
本文並不致力於詳盡的講解每一個組成部分,只想澄清最關鍵的一些概念。一些組成部分的細節(如具體的函數)可以去查閱官方文檔。以上各個部分其實在官方文檔上都有提及,但是散落在不同的頁面,不是很好理解其中的關係。
Vectors
Vector 是什麼
Vector 直接翻譯是向量的意思,PromQL 中的 Vector 也可以理解爲向量。
以一個時間序列(TimeSeries)爲例
counter{a="b", c="d"}
等價於
{a="b", c="d", __name__="counter"}
-
每一個標籤(label)都是一個維度(dimension ),這是一個包含三個維度(dimension)的向量(vector)
-
具有相同 dimension 的 vector 可以認爲有相同的秩(線性代數的概念),可以進行匹配等操作。
-
每個 dimension 都有一個標籤值,所有 unique 的標籤 && 標籤值的組合都是一個獨立的時間序列(TimeSeries)
-
使用 selector(下面會講到)查詢出的可以認爲是全維度(full-dimension)的 vector。當執行某些運算或函數的時候,可能出現維度丟失。例如
-
sum by (a) (counter{a="b", c="d"}) 聚合後的 vector 就只剩下了 a 這一個 dimension
-
官網未提及 irate(counter{a="b", c="d"}[2m]) 這樣的函數,運算完之後,實際上
__name__
已經消失了 -
通過 vector() 函數也可以創建 vector,是一個零維度的 vector
Instant Vector && Range Vector
剛剛講的 dimension 可以理解爲 向量的方向,向量還有一個元素就是向量值,在 Prometheus 中,向量值都是浮點型的數字。
在一個時刻有一個向量值的,就叫做 instant vector
在一個時刻,不僅包含當前時刻的值,還包含前向一段時間範圍的 向量值(確切的說是時刻=>值
的鍵值對)的,就叫做 range vector
對於 instant vector,加上時間維度後,可以很容易地畫出圖像,橫座標是時間戳,縱座標是向量值。
對於 range vector,每一個時刻,都包含一組鍵值對信息,例如
以 example_metric{job="C"} 爲例,可以看到 1@1(value@timestamp) 這個點,出現在了上述所有時刻的 ran ge 裏。
在做運算的時候,可能會用到部分或全部這些信息。例如
-
rate、irate 函數,計算變化率,既需要知道 value 的變化,也需要知道 時間的變化。區別是 rate 會取一個 range 的首和尾來計算,irate 會取一個 range 的最新的兩個點來計算。這也是爲什麼 irate 要比 rate 的圖像變化波動更大。
-
max_over_time 函數,只需要知道一段 range 裏的 value 就行了,並不關心時間戳
Vector 擴展——相關注意點
-
指標在收集的過程中,可能會丟失某些時刻的 sample,一個 range 裏也就會丟失部分數據點。丟失數據點後的 range 在計算過程中,Prometheus 會進行一定的兼容處理(如根據一定算法推測缺失的點)。
-
指標在採集的時候,經常是固定時間間隔採集一次(例如 15s、30s、1m 等),相同 dimension 的指標,在採集時可能會有時間差(前後錯開幾秒)。Prometheus 內部會有時間戳對齊機制,將相應有偏差的採集點對齊後,再做計算。
上述兩點,是想說明 Prometheus 適合用於趨勢類監控,並不能做到十分精確。在某一項指標的具體一小段時間,尤其是 irate 這樣的函數結果並不能精確的反應真實情況。不要用 Prometheus 做時間靈敏度、精確度高的監控手段。
另外,基於採樣的監控,採樣間隔期間出現的瞬時峯值也是無法監控到的。
Selectors
Selector 是什麼
Selector——選擇器——是一個基於標籤匹配來獲取符合條件的 timeseries 的 PromQL 對象
Selector 可以定義一組 label 及其對應的匹配規則,一共有四種:
-
= literally match
-
!= literally not match
-
=~ regularly match
-
!~ regularly not match
標籤的匹配和存儲是基於倒排索引來實現的。
指標名稱也是標籤,是一個特殊的標籤__name__
。
Prometheus 的正則匹配是對 label 值的完全匹配,不支持部分匹配。正則匹配支持的規則也有一定限制(與 go 官方的 regex 庫支持範圍一致),如無法支持 “look behind” 等。
Selector 和 Vector 的關係
相同點
-
Selector 查詢出來的其實是 TimeSeries,也是包含了這個 TimeSeries 所有維度的 “full-dimensional” vector
-
Selector 和 Vector 都有 Instant 和 Range 兩類。Instant Query Selector 可以得到 Instant Vector,Range Query Selector 可以得到 Range Vector
不同點
-
Instant Query Selector 後面加上
[range:resolution]
就變成了 Range Query Selector,其中 冒號和 resolution 可以省略Instant Vector 後面加上 [range:resolution] 就變成了 Range Vector(也叫做 SubQuery),其中 resolution 可以省略,但是冒號不能省略(後文有詳細說明)
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