prometheus之查詢語言

思維無界限發表於2022-04-02

  PromQL(Prometheus Query Language)是 Prometheus 自己開發的表示式語言,語言表現力很豐富,內建函式也很多。使用它可以對時序資料進行篩選和聚合。

一、PromQL

  官方文件:https://prometheus.io/docs/prometheus/latest/querying/basics/#examples

 

  一)資料型別

  promQL表示式計算出來的值有以下幾種型別

  • 瞬時向量 (Instant vector): 一組時序,每個時序只有一個取樣值
  • 區間向量 (Range vector): 一組時序,每個時序包含一段時間內的多個取樣值
  • 標量資料 (Scalar): 一個浮點數

標量只有一個數字,沒有時序。

需要注意的是,當使用表示式count(http_requests_total),返回的資料型別,依然是瞬時向量。

使用者可以通過內建函式scalar()將單個瞬時向量轉換為標量。

  • 字串 (String): 一個字串,暫時未用

 

  二)時序選擇器

  1、瞬時向量選擇器

  瞬時向量選擇器用來選擇一組時序在某個取樣點的取樣值。

  最簡單的情況就是指定一個度量指標,選擇出所有屬於該度量指標的時序的當前取樣值。比如下面的表示式:

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: 打頭的時序。

  2、區間向量選擇器

  區間向量選擇器類似於瞬時向量選擇器,不同的是它選擇的是過去一段時間的取樣值。可以通過在瞬時向量選擇器後面新增包含在 [] 裡的時長來得到區間向量選擇器。比如下面的表示式選出了所有度量指標為 http_requests_totaljobprometheus 的時序在過去 5 分鐘的取樣值。

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

  說明:時長的單位可以是下面幾種之一:

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

  3、偏移修飾器

  選擇器預設都是以當前時間為基準時間,偏移修飾器用來調整基準時間,使其往前偏移一段時間。偏移修飾器緊跟在選擇器後面,使用 offset 來指定要偏移的量。比如下面的表示式選擇度量名稱為 http_requests_total 的所有時序在 5 分鐘前的取樣值。

http_requests_total offset 5m

  下面的表示式選擇 http_requests_total 度量指標在 1 周前的這個時間點過去 5 分鐘的取樣值。

 

http_requests_total[5m] offset 1w

二、PromQL操作符

  一)二元操作符

  PromQL 的二元操作符支援基本的邏輯和算術運算,包含算術類、比較類和邏輯類三大類

  1、算數類二元操作符

  算數類二元操作符以下幾種

  • +:加
  • -:減
  • *:乘
  • /:除
  • %:求餘
  • ^:乘方

  算術類二元操作符可以使用在標量與標量、向量與標量,以及向量與向量之間。

  二元操作符上下文裡的向量特指瞬時向量,不包括區間向量。

  • 標量與標量之間,結果很明顯,跟通常的算術運算一致。
  • 向量與標量之間,相當於把標量跟向量裡的每一個標量進行運算,這些計算結果組成了一個新的向量。
  • 向量與向量之間,會稍微麻煩一些。運算的時候首先會為左邊向量裡的每一個元素在右邊向量裡去尋找一個匹配元素(匹配規則後面會講),然後對這兩個匹配元素執行計算,這樣每對匹配元素的計算結果組成了一個新的向量。如果沒有找到匹配元素,則該元素丟棄。

  2、比較類二元操作符

  比較類二元操作符有以下幾種:

  • == (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。

  3、邏輯類二元操作符

  邏輯操作符僅用於向量與向量之間。

  • and:交集
  • or:合集
  • unless:補集

  具體運算規則如下:

  • vector1 and vector2 的結果由在 vector2 裡有匹配(標籤鍵值對組合相同)元素的 vector1 裡的元素組成。
  • vector1 or vector2 的結果由所有 vector1 裡的元素加上在 vector1 裡沒有匹配(標籤鍵值對組合相同)元素的 vector2 裡的元素組成。
  • vector1 unless vector2 的結果由在 vector2 裡沒有匹配(標籤鍵值對組合相同)元素的 vector1 裡的元素組成。

  4、二元操作符優先順序

  PromQL 的各類二元操作符運算優先順序如下:

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

  二)向量匹配

  前面算術類和比較類操作符都需要在向量之間進行匹配。共有兩種匹配型別,one-to-one 和 many-to-one / one-to-many

  1、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 的沒有匹配元素所以沒有出現在結果裡。

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

   這種匹配模式下,某一邊會有多個元素跟另一邊的元素匹配。這時就需要使用 group_leftgroup_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

  三)聚合操作符

  PromQL 的聚合操作符用來將向量裡的元素聚合得更少。總共有下面這些聚合操作符:

  • sum:求和
  • min:最小值
  • max:最大值
  • avg:平均值
  • stddev:標準差
  • stdvar:方差
  • count:元素個數
  • count_values:等於某值的元素個數
  • bottomk:最小的 k 個元素
  • topk:最大的 k 個元素
  • quantile:分位數

  without和by的使用

  • without用於從計算結果中移除列舉的標籤,而保留其它標籤。
  • by則正好相反,結果向量中只保留列出的標籤,其餘標籤則移除。通過without和by可以按照樣本的問題對資料進行聚合。

三、常用函式

  一些函式有預設的引數,例如:year(v=vector(time()) instant-vector)。v是引數值,instant-vector是引數型別。vector(time())是預設值。

  abs()

  abs(v instant-vector)返回輸入向量的所有樣本的絕對值。

  absent()

 absent(v instant-vector),如果賦值給它的向量具有樣本資料,則返回空向量;如果傳遞的瞬時向量引數沒有樣本資料,則返回不帶度量指標名稱且帶有標籤的樣本值為1的結果

當監控度量指標時,如果獲取到的樣本資料是空的, 使用absent方法對告警是非常有用的

absent(nonexistent{job="myjob"}) # => key: value = {job="myjob"}: 1

absent(nonexistent{job="myjob", instance=~".*"}) # => {job="myjob"} 1 so smart !

absent(sum(nonexistent{job="myjob"})) # => key:value {}: 0

   ceil()

  ceil(v instant-vector) 是一個向上舍入為最接近的整數。

  changes()

  changes(v range-vector) 輸入一個範圍向量, 返回這個範圍向量內每個樣本資料值變化的次數。

  clamp_max()

  clamp_max(v instant-vector, max scalar)函式,輸入一個瞬時向量和最大值,樣本資料值若大於max,則改為max,否則不變

  clamp_min()

  clamp_min(v instant-vector)函式,輸入一個瞬時向量和最大值,樣本資料值小於min,則改為min。否則不變

  count_saclar()

  count_scalar(v instant-vector) 函式, 輸入一個瞬時向量,返回key:value="scalar": 樣本個數。而count()函式,輸入一個瞬時向量,返回key:value=向量:樣本個數,其中結果中的向量允許通過by條件分組。

  day_of_month()

  day_of_month(v=vector(time()) instant-vector)函式,返回被給定UTC時間所在月的第幾天。返回值範圍:1~31。

  day_of_week()

  day_of_week(v=vector(time()) instant-vector)函式,返回被給定UTC時間所在周的第幾天。返回值範圍:0~6. 0表示星期天。

  days_in_month()

  days_in_month(v=vector(time()) instant-vector)函式,返回當月一共有多少天。返回值範圍:28~31.

  delta()

  delta(v range-vector)函式,計算一個範圍向量v的第一個元素和最後一個元素之間的差值。返回值:key:value=度量指標:差值

  下面這個表示式例子,返回過去兩小時的CPU溫度差:

delta(cpu_temp_celsius{host="zeus"}[2h])

 

  delta函式返回值型別只能是gauges。

  deriv()

  deriv(v range-vector)函式,計算一個範圍向量v中各個時間序列二階導數,使用簡單線性迴歸

  deriv二階導數返回值型別只能是gauges。

  drop_common_labels()

  drop_common_labels(instant-vector)函式,輸入一個瞬時向量,返回值是key:value=度量指標:樣本值,其中度量指標是去掉了具有相同標籤。 例如:http_requests_total{code="200", host="127.0.0.1:9090", method="get"} : 4, http_requests_total{code="200", host="127.0.0.1:9090", method="post"} : 5, 返回值: http_requests_total{method="get"} : 4, http_requests_total{code="200", method="post"} : 5

  exp()

  exp(v instant-vector)函式,輸入一個瞬時向量, 返回各個樣本值的e指數值,即為e^N次方。特殊情況如下所示:

Exp(+inf) = +Inf Exp(NaN) = NaN

 

  floor()

  floor(v instant-vector)函式,與ceil()函式相反。 4.3 為 4 。

  histogram_quantile()

  histogram_quatile(φ float, b instant-vector) 函式計算b向量的φ-直方圖 (0 ≤ φ ≤ 1) 。參考中文文獻[https://www.howtoing.com/how-to-query-prometheus-on-ubuntu-14-04-part-2/]

  holt_winters()

  holt_winters(v range-vector, sf scalar, tf scalar)函式基於範圍向量v,生成事件序列資料平滑值。平滑因子sf越低, 對老資料越重要。趨勢因子tf越高,越多的資料趨勢應該被重視。0< sf, tf <=1。 holt_winters僅用於gauges

  hour()

  hour(v=vector(time()) instant-vector)函式返回被給定UTC時間的當前第幾個小時,時間範圍:0~23。

  idelta()

  idelta(v range-vector)函式,輸入一個範圍向量,返回key: value = 度量指標: 每最後兩個樣本值差值。

  increase()

  increase(v range-vector)函式, 輸入一個範圍向量,返回:key:value = 度量指標:last值-first值,自動調整單調性,如:服務例項重啟,則計數器重置。與delta()不同之處在於delta是求差值,而increase返回最後一個減第一個值,可為正為負。

  下面的表示式例子,返回過去5分鐘,連續兩個時間序列資料樣本值的http請求增加值。

increase(http_requests_total{job="api-server"}[5m])
  increase的返回值型別只能是counters,主要作用是增加圖表和資料的可讀性,使用rate記錄規則的使用率,以便持續跟蹤資料樣本值的變化。

  rate()

  rate(v range-vector)函式, 輸入:範圍向量,輸出:key: value = 不帶有度量指標,且只有標籤列表:(last值-first值)/時間差s

rate(http_requests_total[5m])
結果:
{code="200",handler="label_values",instance="120.77.65.193:9090",job="prometheus",method="get"} 0
{code="200",handler="query_range",instance="120.77.65.193:9090",job="prometheus",method="get"}  0
{code="200",handler="prometheus",instance="120.77.65.193:9090",job="prometheus",method="get"}   0.2
{code="200",handler="query",instance="120.77.65.193:9090",job="prometheus",method="get"}    0.003389830508474576
{code="422",handler="query",instance="120.77.65.193:9090",job="prometheus",method="get"}    0
{code="200",handler="static",instance="120.77.65.193:9090",job="prometheus",method="get"}   0
{code="200",handler="graph",instance="120.77.65.193:9090",job="prometheus",method="get"}    0
{code="400",handler="query",instance="120.77.65.193:9090",job="prometheus",method="get"}    0

 

  rate()函式返回值型別只能用counters, 當用圖表顯示增長緩慢的樣本資料時,這個函式是非常合適的。

  注意:當rate函式和聚合方式聯合使用時,一般先使用rate函式,再使用聚合操作, 否則,當服務例項重啟後,rate無法檢測到counter重置。

  irate

  irate(v range-vector)函式, 輸入:範圍向量,輸出:key: value = 度量指標: (last值-last前一個值)/時間戳差值。它是基於最後兩個資料點,自動調整單調性,
  如:服務例項重啟,則計數器重置。  
  下面表示式針對範圍向量中的每個時間序列資料,返回兩個最新資料點過去5分鐘的HTTP請求速率。
irate(http_requests_total{job="api-server"}[5m])

  irate只能用於繪製快速移動的計數器。因為速率的簡單更改可以重置FOR子句,利用警報和緩慢移動的計數器,完全由罕見的尖峰組成的圖形很難閱讀。

  label_replace()

  對於v中的每個時間序列,label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string) 將正規表示式與標籤值src_label匹配。如果匹配,則返回時間序列,標籤值dst_label被替換的擴充套件替換。$1替換為第一個匹配子組,$2替換為第二個等。如果正規表示式不匹配,則時間序列不會更改。

  另一種更容易的理解是:label_replace函式,輸入:瞬時向量,輸出:key: value = 度量指標: 值(要替換的內容:首先,針對src_label標籤,對該標籤值進行regex正規表示式匹配。如果不能匹配的度量指標,則不發生任何改變;否則,如果匹配,則把dst_label標籤的標籤紙替換為replacement 下面這個例子返回一個向量值a帶有foo標籤:label_replace(up{job="api-server", serice="a:c"}, "foo", "$1", "service", "(.*):.*")

  ln()

  ln(v instance-vector)計算瞬時向量v中所有樣本資料的自然對數。特殊例子:

ln(+Inf) = +Inf ln(0) = -Inf ln(x<0) = NaN ln(NaN) = NaN

 

  log2()

  log2(v instant-vector)函式計算瞬時向量v中所有樣本資料的二進位制對數。

  log10()

  log10(v instant-vector)函式計算瞬時向量v中所有樣本資料的10進位制對數。相當於ln()

  minute()

  minute(v=vector(time()) instant-vector)函式返回給定UTC時間當前小時的第多少分鐘。結果範圍:0~59。

  month()

  month(v=vector(time()) instant-vector)函式返回給定UTC時間當前屬於第幾個月,結果範圍:0~12。

  predict_linear()

  predict_linear(v range-vector, t scalar)預測函式,輸入:範圍向量和從現在起t秒後,輸出:不帶有度量指標,只有標籤列表的結果值。

例如:predict_linear(http_requests_total{code="200",instance="120.77.65.193:9090",job="prometheus",method="get"}[5m], 5)
結果:
{code="200",handler="query_range",instance="120.77.65.193:9090",job="prometheus",method="get"}  1
{code="200",handler="prometheus",instance="120.77.65.193:9090",job="prometheus",method="get"}   4283.449995397104
{code="200",handler="static",instance="120.77.65.193:9090",job="prometheus",method="get"}   22.99999999999999
{code="200",handler="query",instance="120.77.65.193:9090",job="prometheus",method="get"}    130.90381188596754
{code="200",handler="graph",instance="120.77.65.193:9090",job="prometheus",method="get"}    2
{code="200",handler="label_values",instance="120.77.65.193:9090",job="prometheus",method="get"} 2

 

  resets()

  resets()函式, 輸入:一個範圍向量,輸出:key-value=沒有度量指標,且有標籤列表[在這個範圍向量中每個度量指標被重置的次數]。在兩個連續樣本資料值下降,也可以理解為counter被重置。 示例:

resets(http_requests_total[5m])
結果:
{code="200",handler="label_values",instance="120.77.65.193:9090",job="prometheus",method="get"} 0
{code="200",handler="query_range",instance="120.77.65.193:9090",job="prometheus",method="get"}  0
{code="200",handler="prometheus",instance="120.77.65.193:9090",job="prometheus",method="get"}   0
{code="200",handler="query",instance="120.77.65.193:9090",job="prometheus",method="get"}    0
{code="422",handler="query",instance="120.77.65.193:9090",job="prometheus",method="get"}    0
{code="200",handler="static",instance="120.77.65.193:9090",job="prometheus",method="get"}   0
{code="200",handler="graph",instance="120.77.65.193:9090",job="prometheus",method="get"}    0
{code="400",handler="query",instance="120.77.65.193:9090",job="prometheus",method="get"}    0

 

  resets只能和counters一起使用。

  round()

  round(v instant-vector, to_nearest 1= scalar)函式,與ceilfloor函式類似,輸入:瞬時向量,輸出:指定整數級的四捨五入值, 如果不指定,則是1以內的四捨五入。

  scalar()

  scalar(v instant-vector)函式, 輸入:瞬時向量,輸出:key: value = "scalar", 樣本值[如果度量指標樣本數量大於1或者等於0, 則樣本值為NaN, 否則,樣本值本身]

  sort()

  sort(v instant-vector)函式,輸入:瞬時向量,輸出:key: value = 度量指標:樣本值[升序排列]

  sort_desc()

  sort(v instant-vector函式,輸入:瞬時向量,輸出:key: value = 度量指標:樣本值[降序排列]

  sqrt()

  sqrt(v instant-vector)函式,輸入:瞬時向量,輸出:key: value = 度量指標: 樣本值的平方根

  time()

  time()函式,返回從1970-01-01到現在的秒數,注意:它不是直接返回當前時間,而是時間戳

  vector()

  vector(s scalar)函式,返回:key: value= {}, 傳入引數值

  year()

  year(v=vector(time()) instant-vector), 返回年份。

  _over_time()

  下面的函式列表允許傳入一個範圍向量,返回一個帶有聚合的瞬時向量:

  • avg_over_time(range-vector): 範圍向量內每個度量指標的平均值。
  • min_over_time(range-vector): 範圍向量內每個度量指標的最小值。
  • max_over_time(range-vector): 範圍向量內每個度量指標的最大值。
  • sum_over_time(range-vector): 範圍向量內每個度量指標的求和值。
  • count_over_time(range-vector): 範圍向量內每個度量指標的樣本資料個數。
  • quantile_over_time(scalar, range-vector): 範圍向量內每個度量指標的樣本資料值分位數,φ-quantile (0 ≤ φ ≤ 1)
  • stddev_over_time(range-vector): 範圍向量內每個度量指標的總體標準偏差。
  • `stdvar_over_time(range-vector): 範圍向量內每個度量指標的總體標準方差

相關文章