PromQL(Prometheus Query Language)為Prometheus tsdb的查詢語言。是結合grafana進行數據展示和告警規則的配置的關鍵部分。
本文默認您已了解Prometheus的四種指標類型:
- counter(計數器)
- gauge (儀表類型)
- histogram(直方圖類型)
- summary (摘要類型)
便於讀者實踐,本文大部分樣本數據target:
- Prometheus
- node_exporter
表達式數據類型
PromQL查詢語句即表達式,實現的四種數據類型:
Instant vector
Instance vector(瞬時向量)表示一個時間序列的集合,但是每個時序只有最近的一個點,而不是線。

Range vector
Range vector(范圍向量)表示一段時間范圍里的時序,每個時序可包含多個點

sources:Understanding Prometheus Range Vectors
Scalar
Scalar(標量)通常為數值,可以將只有一個時序的Instance vector轉換成Scalar。
String
簡單字符串值,目前未被使用。
選擇器
標簽選擇器
查詢Prometheus http狀態碼為400的請求數量。
prometheus_http_requests_total{code="400"}

標簽匹配運算符:
=:與字符串匹配!=:與字符串不匹配=~:與正則匹配!~:與正則不匹配
查詢Prometheus http狀態碼為4xx或5xx並且handler為/api/v1/query的請求數量
prometheus_http_requests_total{code=~"4.*|5.*",handler="/api/v1/query"}
內部標簽__name__用來匹配指標名稱,下面的表達式與上一條等價
{code=~"4.*|5.*",handler="/api/v1/query",__name__="prometheus_http_requests_total"}
范圍選擇器
查詢過去5分鍾Prometheus健康檢查的采樣記錄。
prometheus_http_requests_total{code="200",handler="/-/healthy"}[5m]

單位:ms、s、m、h、d、w、y
時間串聯:[1h5m]一小時5分鍾
時間偏移
通過offset
通過offset將時間倒退5分鍾,即查詢5分鍾之前的數據。
prometheus_http_requests_total{code="200"} offset 5m

同樣支持查詢range vector
prometheus_http_requests_total{code="200"}[3m] offset 5m
@修飾符
還可以通過@ 直接跳轉到某個uinx時間戳,需開啟啟動參數--enable-feature=promql-at-modifier
prometheus_http_requests_total{code="200"} @ 1646089826
運算符
Prometheus中的運算符與各類編程語言中的基本一致。
數學運算符
Prometheus 中存在以下數學運算符:
+(加法)-(減法)*(乘法)/(除法)%(取模)^(冪)
兩個標量之間的計算
10/3

瞬時向量與標量計算,由於計算后值意義與原指標名有差異,Prometheus很貼心的幫我們移除了指標名稱。
prometheus_http_response_size_bytes_sum / 1024

兩個瞬時向量間的計算,如下計算node的內存使用率
(
1 -
node_memory_MemAvailable_bytes{job="node",instance="localhost:9100"}
/ node_memory_MemTotal_bytes{job="node",instance="localhost:9100"}
)
* 100

如果兩個瞬時向量標簽不一致可通過ignoring忽略多余標簽
輸入示例:
method_code:http_errors:rate5m{method="get", code="500"} 24
method_code:http_errors:rate5m{method="post", code="500"} 6
method:http_requests:rate5m{method="get"} 600
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
如果兩個瞬時向量數量不一致時可通過group_left、group_right指定以那一側為准
輸入示例:
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
查詢示例:
group_left以左側為准
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m
結果示例:
{method="get", code="500"} 0.04 // 24 / 600
{method="get", code="404"} 0.05 // 30 / 600
{method="post", code="500"} 0.05 // 6 / 120
{method="post", code="404"} 0.175 // 21 / 120
比較運算符
Prometheus 中存在以下比較運算符:
==(相等)!=(不相等)>(大於)<(小於)>=(大於或等於)<=(小於或等於)
兩個標量之間比較,在運算符后跟bool修飾,結果0( false) 或1 ( true)
10 < bool 5

瞬時向量與標量比較,查詢node狀態
up{job="node"} == bool 1

兩個瞬時向量比較,查看消息隊列容量狀態
prometheus_notifications_queue_length < bool prometheus_notifications_queue_capacity

邏輯運算符
Prometheus 中存在以下邏輯運算符:
and(與)or(或)unless(非)
邏輯運算僅適用於向量
如下我們有4個target,進行相應的邏輯運算,實現和標簽選擇相似效果。

up{instance!="192.168.1.123:9091"} and up{job!="alertmanager"}

up{instance="192.168.1.123:9091"} or up{job="alertmanager"}

up unless up{job="alertmanager"}

Prometheus 中二元運算符的優先級,從高到低。
^*,/,%,atan2+,-==,!=,<=,<,>=,>and,unlessor
相同優先級的運算符是左結合的
聚合運算符
Prometheus 支持以下內置聚合運算符,可用於聚合單個瞬時向量,生成新的向量:
-
sum(總和) -
min(最小) -
max(最大) -
avg(平均值) -
group(分組) -
stddev(標准偏差) -
stdvar(標准方差) -
count(計算向量中的元素個數) -
count_values(計算具有相同值的元素個數) -
bottomk(樣本值的最小 k 個元素) -
topk(按樣本值計算的最大 k 個元素) -
quantile(分位數計算 φ-quantile (0 ≤ φ ≤ 1)
聚合運算符可通過 without、by 根據標簽擴展
sum、min、max、avg:
計算http請求的總和,最大、最小請求的url的數量,平均數量
sum(prometheus_http_requests_total)

通過狀態碼分別統計

group:
類uniq的用法

stddev、stdvar:
反映一組數據離散程度,用以衡量數據值偏離算術平均值的程度。標准偏差為方差的開平方,標准偏差越小,這些值偏離平均值就越少,反之亦然。
通過標准差來反映網絡波動
stddev(rate(node_network_transmit_bytes_total[5m]))
rate計算某段時間的速率

count、count_values:
統計總共有幾個時序
count(prometheus_http_requests_total)

計算每個value的數量
count_values("value",prometheus_http_requests_total)

bottomk、topk
計算value中最小的5個時序
bottomk(5,prometheus_http_requests_total)

quantile:求數據的分位數
我們現在要找出K8s集群中所有node節點的內存使用率的分布情況
quantile
(0.8,
(
1 -
node_memory_MemAvailable_bytes{job="kubernetes-service-endpoints"}
/ node_memory_MemTotal_bytes{job="kubernetes-service-endpoints"}
)
* 100
)

直接可以看出80%的節點內存使用率在68%以下
函數
值取整
ceil()
ceil(v instant-vector)樣本數據向上取整。
ceil(node_load1) #1.2-->2
floor()
floor(v instant-vector)與ceil()相反,floor()樣本值向下取整。
round()
round(v instant-vector, to_nearest=1 scalar) 對樣本值四舍五入取整。to_nearest 參數是可選的,默認為 1,表示樣本返回的是最接近 1 的整數倍的值,參數可以為分數。
取整
round(prometheus_engine_query_duration_seconds_sum)
取整到最近的5的倍數
round(prometheus_engine_query_duration_seconds_sum,5)
值截取
clamp()
clamp(v instant-vector, min scalar, max scalar) 截取所有元素的樣本值在 [min,max]集合內的樣本,如果min>max返回NaN
放回樣本值在10到20的樣本
clamp(prometheus_http_requests_total,10,20)
clamp_max()
clamp_max(v instant-vector, max scalar) 同clamp(),不過只限定樣本最大值
clamp_min()
clamp_min(v instant-vector, min scalar) 同clamp(),不過只限定樣本最小值
值變化統計
changes()
changes(v range-vector)返回某段時間內樣本值改變的次數
changes(node_load1[1m])
復位統計
resets()
resets(v range-vector) 返回樣本范圍時間內的復位次數。與counter使用,兩個連續樣本之間值如有減少則被視為計數器復位。
查看上下文交換次數計數器在5分鍾內復位次數
resets(node_context_switches_total[5m])
日期與時間管理
day_of_month()
day_of_month(v=vector(time()) instant-vector)如果樣本值是utc時間,則返回這個時間所屬月份中的日期(1-31)
v=vector(time()) 為默認參數
day_of_month(node_boot_time_seconds)
day_of_week()
day_of_week(v=vector(time()) instant-vector) 同上,如果樣本值是utc時間,則返回這個時間所屬星期幾(0-6)
days_in_month()
days_in_month(v=vector(time()) instant-vector) 如果樣本值是utc時間,則返回這個時間所屬月份的天數(28-31)
hour()
hour(v=vector(time()) instant-vector)如果樣本值是utc時間,則返回這個時間所屬一天中的第幾個小時(1-13)
minute()
minute(v=vector(time()) instant-vector) 如果樣本值是utc時間,則返回這個時間所屬小時中的第幾分鍾(1-59)
month()
month(v=vector(time()) instant-vector)如果樣本值是utc時間,則返回這個時間所屬的月份(1-12)
year()
year(v=vector(time()) instant-vector)如果樣本值是utc時間,則返回這個時間所屬的年份
time()
返回自1970 年 1 月 1 日 UTC 以來的秒數,不是系統時間,而是表達式計算時那一刻的時間。
timestamp()
timestamp(v instant-vector)返回每個樣本值的時間戳,自 1970 年 1 月 1 日 UTC 以來的秒數。
直方圖分位數
histogram_quantile()
histogram_quantile(φ float, b instant-vector) 從 bucket 類型的向量 b 中計算 φ (0 ≤ φ ≤ 1) 分位數的樣本的最大值,與聚合運算符quantile相似。
計算80%請求的持續時間最大值。
histogram_quantile(0.8,rate(prometheus_http_request_duration_seconds_bucket[1d]))
差異與增長率
delta()
delta(v range-vector)計算范圍向量中每個時間序列元素的第一個值和最后一個值之間的差。與指標類型gauge一起使用
計算一天內內存可用量的變化
delta(node_memory_MemAvailable_bytes[1d])
idelta()
idelta(v range-vector)計算范圍向量中最后兩個樣本之間的差異。與指標類型gauge一起使用
idelta(node_memory_MemAvailable_bytes[1m])
increase()
increase(v range-vector) 計算時間范圍內的增量,與counter一起使用。它是速率rate(v)乘以時間范圍內秒數的語法糖,主要用於人類可讀性。
計算10分鍾內請求增長量
increase(prometheus_http_requests_total[10m])
rate()
rate(v range-vector)計算范圍向量中時間序列的平均每秒增長率。
過去10分鍾請求平均每秒增長率,與counter一起使用。
rate(prometheus_http_requests_total[10m])
irate()
irate(v range-vector) 通過時間范圍的最后兩個點來計算每秒瞬時增長率。
irate(prometheus_http_requests_total[10m])
label管理
label_join()
label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...)為每個時間序列添加一個label,值為指定舊label的value連接
label_join(up{instance="localhost:9100", job="node"},"new_label","-","instance","job")
結果:
up{instance="localhost:9100", job="node", new_label="localhost:9100-node"} 1
label_replace()
label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string)從源label中獲取value元素用於添加新的label
$1 獲取正則匹配,匹配值添加到hello標簽中
label_replace(up{instance="localhost:9100", job="node"},"hello","$1","job","(.*)")
結果:
up{hello="node", instance="localhost:9100", job="node"} 1
預測
predict_linear()
predict_linear(v range-vector, t scalar) 通過簡單線性回歸預測t秒后的樣本值,與gauge一起使用。
根據過去1小時的文件系統剩余空間量,預測1小時之后的剩余空間
predict_linear(node_filesystem_free_bytes[1h],3600)
轉換
absent()
absent(v instant-vector)如果向量有元素,則返回一個空向量;如果向量沒有元素,則返回值為 1。
設置如下告警表達式:
absent(up{job="node"} == 1)
由於up{job="node"} 不存在或值不為1則告警表達式的值為1 產生告警
absent_over_time()
absent_over_time(v range-vector)如果范圍向量有元素,則返回一個空向量;如果范圍向量沒有元素,則返回值為 1。
如果up{job="node1"}在某段時間不存在則返回1
absent_over_time(up{job="node1"}[1h])
scalar()
scalar(v instant-vector)以標量形式返回該單元素的樣本值,如果輸入向量不是正好一個元素,scalar將返回NaN.
vector()
vector(s scalar)將標量作為沒有標簽的向量返回。
sgn()
sgn(v instant-vector)返回一個向量,其中所有樣本值都轉換為1或-1或0
定義如下:
如果 v 為正,則為 1
如果 v 為負,則為 -1
如果 v 等於 0,則為 0。
排序
sort()
sort(v instant-vector)返回按樣本值升序排序的向量元素。
sort_desc()
與sort()相反,按降序排序。
_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): 區間向量內每個度量指標的總體標准方差
數學函數
abs()
abs(v instant-vector) 返回樣本的絕對值。
sqrt()
sqrt(v instant-vector)計算樣本值的平方根。
deriv()
deriv(v range-vector) 使用簡單線性回歸計算時間序列在范圍向量中的每秒導數。與指標類型gauge一起使用
exp()
exp(v instant-vector)計算樣本值的指數函數。
特殊情況:
- Exp(+Inf) = +Inf
- Exp(NaN) = NaN
ln()、log2()、log10()
ln/log2/log10(v instant-vector) 計算樣本值對數
特殊情況(同適用於log2/log10):
ln(+Inf) = +Infln(0) = -Infln(x < 0) = NaNln(NaN) = NaN
holt_winters()
holt_winters(v range-vector, sf scalar, tf scalar)基於訪問向量v,生成時間序列數據平滑數據值。平滑因子sf越低, 對舊數據越重要。趨勢因子tf越高,更關心趨勢數據。0<sf,tf<=1。 與gauge一起使用
三角函數、弧度
acos(v instant-vector)acosh(v instant-vector)asin(v instant-vector)asinh(v instant-vector)atan(v instant-vector)atanh(v instant-vector)cos(v instant-vector)cosh(v instant-vector)sin(v instant-vector)sinh(v instant-vector)tan(v instant-vector)tanh(v instant-vector)
角度、弧度轉化
deg(v instant-vector)pi()rad(v instant-vector)
如果內容有誤請指正。通過博客閱讀:iqsing.github.io
參考
[1] Understanding Prometheus Range Vectors: https://satyanash.net/software/2021/01/04/understanding-prometheus-range-vectors.html
[2] promethues: https://prometheus.io/docs/prometheus/latest/querying/basics/
