0x00 概述
Prometheus 提供了一種功能表達式語言 PromQL
,允許用戶實時選擇和匯聚時間序列數據。表達式的結果可以在瀏覽器中顯示為圖形,也可以顯示為表格數據,或者由外部系統通過 HTTP API 調用。
0x01 表達式語言數據類型
在 Prometheus 的表達式語言中,表達式或子表達式包括以下四種類型之一:
-
瞬時向量(Instant vector) - 一組時間序列,每個時間序列包含單個樣本,它們共享相同的時間戳。也就是說,表達式的返回值中只會包含該時間序列中的最新的一個樣本值。而相應的這樣的表達式稱之為瞬時向量表達式。
-
區間向量(Range vector) - 一組時間序列,每個時間序列包含一段時間范圍內的樣本數據。
-
標量(Scalar) - 一個浮點型的數據值。
- 字符串(String) - 一個簡單的字符串值。
根用戶輸入的表達式返回的數據類型是否合法取決於用例的不同,例如:瞬時向量表達式返回的數據類型是唯一可以直接繪制成圖表的數據類型。
0x02 字面量
字符串
字符串可以用單引號、雙引號或反引號指定為文字常量。
PromQL 遵循與 Go 相同的轉義規則。在單引號或雙引號中,用反斜杠來表示轉義序列,后面可以跟 a
, b
, f
, n
, r
, t
, v
或 \
。特殊字符可以使用八進制(\nnn
)或者十六進制(\xnn
,\unnnn
和 \Unnnnnnnn
)。
與 Go 不同,Prometheus 不會對反引號內的換行符進行轉義。
例如:
"this is a string" 'these are unescaped: \n \\ \t' `these are not unescaped: \n ' " \t`
標量
標量浮點值可以字面上寫成 [-](digits)[.(digits)]
的形式。
-2.43
0x03 時間序列過濾器
瞬時向量過濾器
瞬時向量過濾器允許在指定的時間戳內選擇一組時間序列和每個時間序列的單個樣本值。在最簡單的形式中,近指定指標(metric)名稱。這將生成包含此指標名稱的所有時間序列的元素的瞬時向量。
例如:選擇指標名稱為 http_requests_total
的所有時間序列:
http_requests_total
可以通過向花括號({}
)里附加一組標簽來進一步過濾時間序列。
例如:選擇指標名稱為 http_requests_total
,job
標簽值為 prometheus
,group
標簽值為 canary
的時間序列:
http_requests_total{job="prometheus",group="canary"}
PromQL 還支持用戶根據時間序列的標簽匹配模式來對時間序列進行過濾,目前主要支持兩種匹配模式:完全匹配和正則匹配。總共有以下幾種標簽匹配運算符:
-
=
: 選擇與提供的字符串完全相同的標簽。 -
!=
: 選擇與提供的字符串不相同的標簽。 -
=~
: 選擇正則表達式與提供的字符串(或子字符串)相匹配的標簽。 -
!~
: 選擇正則表達式與提供的字符串(或子字符串)不匹配的標簽。
例如:選擇指標名稱為 http_requests_total
,環境為 staging
、testing
或 development
,HTTP 方法為 GET
的時間序列:
http_requests_total{environment=~"staging|testing|development",method!="GET"}
沒有指定標簽的標簽過濾器會選擇該指標名稱的所有時間序列。
所有的 PromQL 表達式必須至少包含一個指標名稱,或者一個不會匹配到空字符串的標簽過濾器。
以下表達式是非法的(因為會匹配到空字符串):
{job=~".*"} # 非法!
以下表達式是合法的:
{job=~".+"} # 合法! {job=~".*",method="get"} # 合法!
除了使用 <metric name>{label=value}
的形式以外,我們還可以使用內置的 __name__
標簽來指定監控指標名稱。例如:表達式 http_requests_total
等效於 {__name__="http_requests_total"}
。也可以使用除 =
之外的過濾器(=
,=~
,~
)。以下表達式選擇指標名稱以 job:
開頭的所有指標:
{__name__=~"job:.*"}
Prometheus 中的所有正則表達式都使用 RE2語法。
0x04 區間向量過濾器
區間向量與瞬時向量的工作方式類似,唯一的差異在於在區間向量表達式中我們需要定義時間選擇的范圍,時間范圍通過時間范圍選擇器 []
進行定義,以指定應為每個返回的區間向量樣本值中提取多長的時間范圍。
時間范圍通過數字來表示,單位可以使用以下其中之一的時間單位:
-
s
- 秒 -
m
- 分鍾 -
h
- 小時 -
d
- 天 -
w
- 周 -
y
- 年
例如:選擇在過去 5 分鍾內指標名稱為 http_requests_total
,job
標簽值為 prometheus
的所有時間序列:
http_requests_total{job="prometheus"}[5m]
0x05 時間位移操作
在瞬時向量表達式或者區間向量表達式中,都是以當前時間為基准:
http_request_total{} # 瞬時向量表達式,選擇當前最新的數據 http_request_total{}[5m] # 區間向量表達式,選擇以當前時間為基准,5分鍾內的數據
而如果我們想查詢,5 分鍾前的瞬時樣本數據,或昨天一天的區間內的樣本數據呢? 這個時候我們就可以使用位移操作,位移操作的關鍵字為 offset
。
例如,以下表達式返回相對於當前查詢時間過去 5 分鍾的 http_requests_total
值:
http_requests_total offset 5m
注意:offset
關鍵字需要緊跟在選擇器({}
)后面。以下表達式是正確的:
sum(http_requests_total{method="GET"} offset 5m) #合法
下面的表達式是不合法的:
sum(http_requests_total{method="GET"}) offset 5m # 錯誤
該操作同樣適用於區間向量。以下表達式返回指標 http_requests_total
一周前的 5 分鍾之內的 HTTP 請求量的增長率:
rate(http_requests_total[5m] offset 1w)
0x06 操作符
使用PromQL除了能夠方便的按照查詢和過濾時間序列以外,PromQL還支持豐富的操作符,用戶可以使用這些操作符對進一步的對事件序列進行二次加工。這些操作符包括:數學運算符,邏輯運算符,布爾運算符等等。詳細描述請參考 PromQL 操作符。
0x07 內置函數
Prometheus 提供了大量的內置函數來處理時序數據,詳細描述請參考 PromQL 內置函數。
0x08 陷阱
失效
執行查詢操作時,獨立於當前時刻被選中的時間序列數據所對應的時間戳,這個時間戳主要用來進行聚合操作,包括 sum
, avg
等,大多數聚合的時間序列數據所對應的時間戳沒有對齊。由於它們的獨立性,我們需要在這些時間戳中選擇一個時間戳,並已這個時間戳為基准,獲取小於且最接近這個時間戳的時間序列數據。
如果采樣目標或告警規則不再返回之前存在的時間序列的樣本,則該時間序列將被標記為失效。如果刪除了采樣目標,則之前返回的時間序列也會很快被標記為失效。
如果在某個時間序列被標記為失效后在該時間戳處執行查詢操作,則不會為該時間序列返回任何值。如果隨后在該時間序列中插入了新的樣本,則照常返回時間序列數據。
如果在采樣時間戳前 5 分鍾(默認情況)未找到任何樣本,則該時間戳不會返回任何任何該時間序列的值。這實際上意味着你在圖表中看到的數據都是在當前時刻 5 分鍾前的數據。
對於在采樣點中包含時間戳的時間序列,不會被標記為失效。在這種情況下,僅使用 5 分鍾閾值檢測的規則。
避免慢查詢和高負載
如果一個查詢需要操作非常大的數據量,圖表繪制很可能會超時,或者服務器負載過高。因此,在對未知數據構建查詢時,始終需要在 Prometheus 表達式瀏覽器的表格視圖中構建查詢,直到結果是看起來合理的(最多為數百個,而不是數千個)。只有當你已經充分過濾或者聚合數據時,才切換到圖表模式。如果表達式的查詢結果仍然需要很長時間才能繪制出來,則需要通過記錄規則重新清洗數據。
像 api_http_requests_total
這樣簡單的度量指標名稱選擇器,可以擴展到具有不同標簽的數千個時間序列中,這對於 Prometheus 的查詢語言是非常重要的。還要記住,對於聚合操作來說,即使輸出的時間序列集非常少,它也會在服務器上產生負載。這類似於在關系型數據庫中查詢一個字段的總和,總是非常緩慢。