引言
在微服務架構和云原生應用中,監控和可觀測性變得至關重要。Prometheus作為一款云原生領域開源監控系統,被廣泛用于收集、存儲及查詢時間序列(Time Series)數據。PromQL(Prometheus Query Language)是Prometheus提供的時間序列數據查詢語言,用于選擇、聚合和計算時間序列數據,允許用戶以靈活、強大的方式查詢、分析監控數據。本文將深入探討PromQL的主要功能、用法、示例以及常見應用場景,幫助讀者更好地理解和使用PromQL。
PromQL概述
PromQL(Prometheus Query Language)是一種功能強大的查詢語言,專為Prometheus設計。它允許用戶以靈活的方式從時間序列數據庫中選擇、提取、聚合和分析數據,支持多種聚合和計算操作。PromQL的設計目標是提供一種簡單且靈活的方式來查詢、分析監控數據,使用戶能夠快速獲取所需的信息。Prometheus提供兩種查詢:瞬時查詢(instant query,查詢某個時間點的數據)、范圍查詢( range query,在開始和結束時間之間以均勻間隔進行數據查詢),可以將范圍查詢看做在不同時間點上多次進行瞬時查詢。
PromQL基本概念
- 時間序列(Time Series):Prometheus中的基本數據單位,表示在特定時間點的度量值。
- 指標(Metric):時間序列名稱,通常表示某種度量。例如,CPU使用率、內存使用量等。
- 標簽(Label):時間序列元數據,用于標識和分類數據。例如,使用標簽來區分不同服務、實例或機房。
數據類型
在Prometheus的表達式語言中,表達式或子表達式表現為如下四種類型:
- 瞬時向量(Instant vector):包含單個數據點的時間序列集合,且所有數據點具有相同的時間戳。
- 范圍向量(Range vector):包含隨時間變化的一系列數據點的時間序列集合。
- 標量(Scalar ):浮點數值。
- 字符串(String):字符串值。
PromQL語法
PromQL的語法相對簡單,主要由以下幾個部分組成:
1. 選擇器(Selector)
選擇器用于指示PromQL要獲取哪些時間序列數據。選擇器的基本格式如下:
metric_name{label_name="value"}
例如,選擇指標名為http_requests_total的指標,并且標簽method的值為GET,可以使用如下查詢選擇器:
http_requests_total{method="GET"}
瞬時向量選擇器
瞬時向量選擇器選擇一組時間序列及其在給定時間戳(時間點)下的單個數據點。在最簡單的形式中,通過指定一個指標名稱,將產生包含所有具有該指標名稱的時間序列的瞬時向量。例如選擇所有具有http_requests_total指標名稱的時間序列:
http_requests_total
可以在指標名稱后面追加大括號({}),在其中添加以逗號(,)分隔的標簽匹配器列表來進一步過濾這些時間序列。
下面的示例是僅選擇那些具有http_requests_total指標名稱且job標簽設置為prometheus,并且group標簽設置為canary的時間序列:
http_requests_total{job="prometheus",group="canary"}
可以對標簽值進行負匹配,或將標簽值與正則表達式進行匹配。有如下標簽匹配運算符:
- =: 與提供字符串完全相等的標簽。
- !=: 與提供的字符串不相等的標簽。
- =~: 與提供的字符串正則匹配的標簽。
- !~: 與提供的字符串正則不匹配的標簽。
- 其中正則匹配是需要完全錨定的,如env =~"foo" 匹配為 env=~"^foo$"。
 例如,選擇環境(environment)標簽為預上線(staging)、測試(testing)和開發(development)的http_requests_total指標,且HTTP方法標簽不是GET的時間序列:
http_requests_total{environment=~"staging|testing|development",method!="GET"}
如果標簽匹配的是空值則該匹配器會選擇所有沒有設置該標簽的時間序列,假如有如下時間序列數據集:
http_requests_total
http_requests_total{replica="rep-a"}
http_requests_total{replica="rep-b"}
http_requests_total{environment="development"}
查詢http_requests_total{environment=""}將匹配如下時間序列:
http_requests_total
http_requests_total{replica="rep-a"}
http_requests_total{replica="rep-b"}
會將如下environment不為空的時間序列排除:
http_requests_total{environment="development"}
對同一標簽可以使用多個匹配器,而且所有匹配器都必須滿足匹配條件才返回。如下查詢:
http_requests_total{replica!="rep-a",replica=~"rep.*"}
將匹配如下時間序列:
http_requests_total{replica="rep-b"}
選擇器必須指定指標名稱或至少一個不匹配空字符串的標簽匹配器。下面的表達式是非法的:
{job=~".*"} # 錯誤
下面這些表達式是有效的,因為它們都有一個不匹配空標簽值的選擇器:
{job=~".+"}              # 正確!
{job=~".*",method="get"} # 正確!
標簽匹配器可以通過匹配內部__name__標簽應用于指標名稱。例如,表達式 http_requests_total等價于{__name__="http_requests_total"}。除了 = 之外,還可以使用其他匹配運算符(!=、=~、!~)。下面的表達式選擇所有名稱以 job: 開頭的指標:
{__name__=~"job:.*"}
指標名稱不能是關鍵字 bool、on、ignoring、group_left 和 group_right 之一。以下表達式是非法的:
on{} # 錯誤
可以使用__name__標簽來解決這個限制:
{__name__="on"} # 正確
注:Prometheus中使用的正則表達式都遵循 RE2 語法。
范圍向量選擇器
范圍向量選擇器的工作方式跟瞬時向量選擇器相似,不同之處在于它會選擇從當前時間戳向后查詢一系列數據點。語法上,在選擇器的末尾附加一個方括號([])來表示持續時間,這個時間值用來指定范圍向量應獲取從當前時間戳向前多遠的元素。范圍向量中的范圍是一個閉區間,即與范圍的任一邊界重合的時間戳的樣本都包含在該范圍向量中。
下面示例中,選擇具有http_requests_total指標名稱且job標簽為prometheus過去5分鐘內所有數據點:
http_requests_total{job="prometheus"}[5m]
持續時間由數字和單位構成,可用單位如下:
- ms - 毫秒
- s - 秒
- m - 分鐘
- h - 小時
- d - 天 - 假設一天始終有 24 小時
- w - 周 - 假設一周始終有 7 天
- y - 年 - 假設一年始終有 365 天
持續時間支持連接組合,單位必須按從長到短的順序排列,且同一個單位在持續時間中只能出現一次。下面是有效的持續時間示例:
5h
 1h30m
 5m
 10s
2.54版本開始,持續時間可以使用浮點數來表示持續時間的秒數。例如:
1.0 # 等價于 1s
0.001 # 等價于 1ms
120 # 等價于 2m
偏移(offset)修飾符
偏移修飾符用于在查詢中更改瞬時和范圍查詢的時間偏移。
例如,查詢 http_requests_total 在當前時間戳前 5 分鐘的值:
http_requests_total offset 5m
注意,偏移修飾符需要緊跟選擇器之后,下面是正確的格式:
sum(http_requests_total{method="GET"} offset 5m) // 正確
下面這個是錯誤的格式:
sum(http_requests_total{method="GET"}) offset 5m // 無效
offset 同樣適用于范圍查詢。http_requests_total 在一周前的 5 分鐘速率:
rate(http_requests_total[5m] offset 1w)
@ 修飾符
@ 修飾符用于在查詢中更改瞬時和范圍查詢的當前時間戳。提供給 @ 修飾符的時間是一個 Unix 時間戳,并用浮點數值描述。
例如,http_requests_total 在 2021-01-04T07:40:00+00:00 的值:
http_requests_total @ 1609746000
注意,@ 修飾符需要緊跟選擇器之后,下面是正確的格式:
sum(http_requests_total{method="GET"} @ 1609746000) // 正確
下面這個是錯誤的格式:
sum(http_requests_total{method="GET"}) @ 1609746000 // 無效
@ 修飾符同樣適用范圍查詢。http_requests_total 在 2021-01-04T07:40:00+00:00 的 5 分鐘速率:
rate(http_requests_total[5m] @ 1609746000)
@ 修飾符可以與偏移修飾符一起工作,這時偏移就應用于 @ 修飾符指定時間。這兩個修飾符之間的順序不會對結果產生影響。
例如,如下兩個查詢將產生相同的結果:
# 在 @ 之后偏移
http_requests_total @ 1609746000 offset 5m
# 在 @ 之前偏移
http_requests_total offset 5m @ 1609746000
此外,start() 和 end() 函數可以作為 @ 修飾符的值來使用。
對于范圍查詢來說,它們分別解析為范圍查詢的開始時間和結束時間。
對于瞬時查詢,start() 和 end() 都解析為當前查詢時間。
http_requests_total @ start()
rate(http_requests_total[5m] @ end())
2. 聚合函數(Aggregate Function)
PromQL提供了多種聚合函數,用于對時間序列數據進行匯總。常用聚合函數包括:
- sum(): 計算總和
- avg(): 計算平均值
- max(): 計算最大值
- min(): 計算最小值
- count(): 計算數量
 例如,計算所有實例的http_requests_total總和,可以使用如下查詢語句:
sum(http_requests_total)
3. 運算符(Operator)
PromQL支持多種運算符,包括加法、減法、乘法和除法。運算符可用于時間序列數據之間的計算。例如,計算CPU使用率與內存使用量的比率,可以使用如下查詢語句:
sum(rate(cpu_usage_seconds_total[5m])) / sum(memory_usage_bytes)
4. 時間函數(Time Function)
PromQL提供了一些時間函數用于處理時間序列數據。常用的時間函數包括:
- rate(): 計算時間序列數據的單位時間變化率
- increase(): 計算時間序列數據的增量
- irate(): 計算瞬時變化率
 例如,計算過去5分鐘內每秒的請求速率,可以使用如下查詢語句:
rate(http_requests_total[5m])
PromQL查詢示例
基本查詢
查詢所有實例的http_requests_total指標:
http_requests_total
聚合查詢
計算所有實例的http_requests_total總和:
sum(http_requests_total)
時間函數查詢
計算過去10分鐘內每秒的請求速率:
rate(http_requests_total[10m])
復雜查詢
計算每個實例CPU使用率,并按實例(instance)進行分組:
sum(rate(cpu_usage_seconds_total[5m])) by (instance)
常見應用場景
- 監控CPU、內存使用情況
 使用PromQL可以輕松監控CPU、內存的使用情況。例如,查詢每個實例過去5分鐘的CPU使用率:
sum(rate(cpu_usage_seconds_total[5m])) by (instance)
- 監控網絡流量
 PromQL還可以用于監控網絡流量。例如,查詢每個實例過去5分鐘的網絡接收、發送字節數:
sum(rate(network_receive_bytes_total[5m])) by (instance)
sum(rate(network_transmit_bytes_total[5m])) by (instance)
- 監控應用程序性能
 通過PromQL可以監控應用程序的性能指標,如請求延遲、錯誤率等。例如,查詢每個實例過去5分鐘的請求延遲的95分位值:
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (instance))
- 監控自定義指標
 PromQL允許監控自定義指標。例如,監控特定業務邏輯的指標,如過去1小時用戶注冊數增量:
sum(increase(user_registration_total[1h]))
性能優化
在使用PromQL時,性能是一個重要的考慮因素。下面是一些常用性能優化技巧:
- 合適的時間查詢范圍:查詢時選擇合適的時間范圍,以避免不必要的數據查詢處理。
- 避免過于復雜的查詢:盡量簡化查詢,避免使用過多的聚合計算和運算符操作。
- 指標數據緩存:對于頻繁查詢的指標,可以考慮使用緩存機制。
 如果需要查詢處理大量數據,頁面繪圖可能會超時或使服務器、瀏覽器過載。因此,在構建未知規模的數據查詢時,先從Prometheus的表格視圖開始構建,直到結果看起來合理(最多數百個時間序列,而不是數千個時間序列)。只有在充分過濾或聚合后,才能切換到圖形視圖。如果仍然需要太長時間才能繪制圖形,建議使用記錄規則進行預先處理。此外,聚合多個時間序列即使輸出只有少量時間序列結果,也會對服務器產生嚴重負載,這類似于在關系數據庫中對一列的所有值求和,即使輸出值只有一個數字,也會很慢。
結論
PromQL是一個簡單、靈活且強大的查詢語言,能夠幫助用戶從Prometheus中提取和分析時間序列監控數據。通過掌握PromQL的基本概念、語法和功能,可以更加有效地監控和優化應用程序、基礎設施。希望本文能幫助您在實際工作中更好地使用PromQL。