女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

詳解Prometheus的數據類型

馬哥Linux運維 ? 來源:CSDN技術社區 ? 2025-05-13 09:50 ? 次閱讀

對于 Prometheus 生態的監控系統,PromQL 是必備技能,本文著重點講解這個查詢語言,摻雜一些生產實踐場景,希望對你有所幫助。

數據類型

Prometheus 有四種數據類型:Gauge、Counter、Histogram、Summary,其中最關鍵的是 Gauge 和 Counter,Histogram 和 Summary 只是為了上報監控數據的 Client 側的便利,可以看做是組合使用了 Gauge 和 Counter。所以我們重點就來講解 Gauge 和 Counter 類型。

Gauge 類型

Gauge 類型的值表示當前的狀態,可大可小、可負可正,比如某個虛機實例掛了,用 0 表示, 如果實例存活,用 1 表示;再比如內存使用率,這個時刻采集是 33.7%,下個周期采集可能就 變成了 25.8%;還有像機器最近 5 分鐘的 load、正在運行的進程數量等等,都使用 Gauge 類 型來表示。這種類型的值,我們非常關注當前值。

Counter 類型

Counter 類型是單調遞增的值,比如機器上某塊網卡收到的數據包的總量,是從操作系統啟動 之后,就持續遞增的,對于這種類型的值,我們通常關注的不是當前值是多少,而是關注增量和 變化率。我們在機器上執行 ifconfig 命令:

eth0: flags=4163 mtu 1500
inet 10.206.0.16 netmask 255.255.240.0 broadcast 10.206.15.255
inet6 fe80:ffa180 prefixlen 64 scopeid 0x20
ether 5200a1:80 txqueuelen 1000 (Ethernet)
RX packets 457952401 bytes 125894899868 (117.2 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 518040495 bytes 276312546157 (257.3 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

RX packets 后面的值是 OS 啟動以來收到的總的包量,TX packets 后面的值是 OS 啟動以來發出去的總的包量,都是很大的值,我們通常不太關注這個值當前是多少,更關注的是最近 1 分鐘收到/發出多少包,或者每秒收到/發出多少包。而對于監控數據采集器而言,一般是周期性運行的,比如每 10 秒采集一次,每次采集網卡收到/發出的包這個數據的時候,都只能采集到當前的值,就像執行 ifconfig 命令,每 10 秒執行一次,每次都看到一個巨大的當前值,而且一次比一次大。如果采集器不做計算,把這個值原封不動上報給監控服務端,那計算增量、計算速率這個需求,就要放到服務端來實現了,所以服務端必須要能對這種類型的數據建模抽象,也就是所謂的 Counter 類型。

時序數據

PromQL 就是查詢時序數據的一種 Query Language,要想對 PromQL 有了解,得先搞清楚時
序數據。

認識時序數據

我們先來看一張圖,圖上是 5 臺機器的內存可用率:![

93771ff0-2b27-11f0-9310-92fbcf53809c.png

](https://img-blog.csdnimg.cn/direct/a8629b58178246378179466d21acb886.png)
每個機器的內存可用率數據,體現為圖上的一條線,我們稱為 series,某個機器在某一時刻的內存可用率數據,我們稱為數據點,比如上圖,2022-08-25 1522 這個時刻,每個機器都有一個可用率數據點,共計 5 個數據點。上面的圖是查詢的最近一小時的,我們切換到 Table 視圖,得到如下結果:

938dab6c-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述 這個表格的內容,是這 5 臺機器在當前這個時間點的最新值,當前我做查詢的時刻是:2022-
08-25 1503 用 Chrome 開發者工具可以看到發的請求參數:

93a639f2-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
但是,監控數據是周期性上報的,比如每 10 秒上報一次,在 2022-08-25 1503 這個時刻,未必恰好有監控數據啊,那這個 Table 中的數據是哪里來的?
實際上,Prometheus 有個啟動參數,–query.lookback-delta=2m 來控制這個行為,如果配置為 2m,就表示,Prometheus 會查詢 2022-08-25 1503 ~ 2022-08-25 1503 這 2分鐘之間的數據,然后返回最新的那個。

查詢類型

上例中的 mem_available_percent{app=“clickhouse”} 稱為查詢表達式,不同的表達式,會返回不同的內容,返回的內容總共有 4 種格式,分別是:Instant vector(瞬時向量)、Rangevector(范圍向量)、Scalar(標量)、String(字符串)。返回瞬時向量的查詢表達式,我們
稱為 Instant Query,返回范圍向量的查詢表達式,我們稱為 Range Query。

上例中的 mem_available_percent{app=“clickhouse”} 既可以用于展示 Table 視圖,又可以用于展示 Graph 視圖,是典型的 Instant Query。

如果在表達式后面加上一個時間范圍,比如 1 分鐘:
mem_available_percent{app=“clickhouse”}[1m] 這個查詢表達式就變成了 Range Query,Range Query里有個時間范圍,其 Table 視圖的截圖如下:

93b9a35c-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
第 4 臺機器相比其他的機器,返回了更多數據,是因為那個機器的監控數據采集頻率是 10s,而其他的機器采集頻率是 30s。

通過 range query + Table 視圖,可以讓我們直觀看到原始上報的監控數據以及上報的具體時刻(對于排查監控數據采集相關的問題尤為有用),如果在 Graph 視圖,返回的數據取決于 step 參數,查詢時傳給時序庫的 step = 10,返回的圖形就是每 10s 一個點,step =20 就是每 20s 一個點,返回的數據的時間間隔取決于 step 參數而非原始數據的上報間隔。

Range Query 理論上是沒法繪制 Graph 的(當然有些時序庫可能會做容錯處理),因為從原理上說不通。繪圖的時候,我們要選擇一個時間范圍,比如最近一小時,然后傳給后端一個step 參數用于控制分辨率,即數據間隔,比如 step=60,即表示希望每個 series 每分鐘返回一個點,但如果是 Range Query,相當于在某個時刻返回多個點,這就無所適從了。

Prometheus 文檔中有一個章節專門介紹函數,各個函數的介紹中,都會寫明是用于 instantvector,還是用于 range-vector,如果不理解查詢類型,就無法很好的應用這些函數。

查詢選擇器

PromQL大括號里的部分是 selector,查詢選擇器,用于從一大堆監控數據中,過濾出真正關心的數據,在 Prometheus 生態里,時序數據的標識,就是一堆標簽集合,所以這里的過濾,就是針對標簽做過濾,支持四類操作符:

? =:完全匹配,比如 app=“clickhouse”

? !=:完全不匹配,比如 app!=“clickhouse”

? =:正則匹配,比如 app=“n9e-.*”

? !:正則不匹配,比如 app!“n9e-.*”

指標名稱,通常放到大括號之外,但實際上,指標名稱也是一個標簽,其標簽Key是__name__,所以之前查詢的例子,可以這么寫:

{__name__="mem_available_percent", app="clickhouse"}

94b49df2-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
仍然可以達成相同的效果。有時采集的監控數據格式設計的不好,一些本該用 label 的信息,放到了 metric 名稱中了,此時就可以用name做一些正則匹配。

Offset

監控系統里,經常會有同環比的需求,比如,當前的值相比一周之前,是否有巨大變化,那怎么才能獲取歷史數據呢?可以使用 offset 關鍵字。
offset 后面跟一個時間段,比如 5m、1d、7d、1w,offset 要緊跟查詢選擇器,比如:

sum(http_requests_total{method="GET"} offset 1d)

運算符

PromQL 支持基本的算術運算符和比較運算符,可以對不同的即時向量做運算,這為監控系統帶來了巨大的進步,算術運算符讓很多計算不需要在采集端做了,可以輕易挪到服務端,而比較運算符則為告警邏輯提供了支撐。

算術運算符

? (+) (addition)

? (-) (subtraction)

? (*) (multiplication)

? (/) (division)

? (%) (modulo)

? (^) (power/exponentiation)

舉一個例子來演示真實環境下的算術運算符的應用,比如之前的例子,對于內存可用率的指標mem_available_percent 這個指標是采集器直接計算好的,如果采集器沒有計算,而是上報了原始指標 mem_available 和 mem_total,我們仍然可以使用 promql 計算出可用率指標:

94d00f7e-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
邏輯上,是先根據 mem_available{app=“clickhouse”} 找到相關指標數據,會找到5條,再根據 mem_total{app=“clickhouse”} 也能找到5條,二者相除的邏輯姑且可以理解為,循環遍歷mem_available 的5條記錄,對于每一條,去 mem_total 的5條記錄中找標簽相同的記錄,進
行除法運算。除法運算得到5條結果(0~1之間的數字),然后跟100相乘(得到百分比大小),100這個數字稱為標量,5條結果和標量計算,會把每一條結果分別乘以100,得到最終的結果,這個最終結果其實就是 mem_available_percent。

如果分子和分母對應的selector查到的數據標簽不同,就沒法做除法運算了,比如
net_bytes_recv 比內存相關的指標多了一個interface的標簽(標明網卡),二者是沒法做運算
的,結果為空:

net_bytes_recv{app="clickhouse"}/mem_total{app="clickhouse"}

比較運算符

? == (equal)
? != (not-equal)
? > (greater-than)
? < (less-than)
? >= (greater-or-equal)
? <= (less-or-equal)
比較運算符的最典型用法,就是一個 instant-vector 和一個標量之間的比較,比如
mem_available_percent{app=“clickhouse”} 的結果:

94ea09c4-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
如果我們認為內存可用率小于60就是有問題的,想找出所有有問題的數據,只要在 promql 中拼上 < 60 即可:

950a7b3c-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述

如上的方法,其實就是告警引擎的核心邏輯。告警規則里會要求用戶配置promql以及執行頻率,告警引擎就會根據執行頻率周期性執行,每次執行的時候就是拿著promql去查詢,promql中帶有閾值,即上例中的 <60,所以如果所有機器的內存可用率都很高,比如維持在80~90,
那這個promql是不會返回查詢結果的,此時監控系統就認為一切正常。如果返回了結果,比如上例中返回了3條結果,告警引擎就會認為有異常產生,生成3個告警事件。

當然,有的時候,偶爾一次觸發了閾值我們認為不算啥事,希望連續觸發多次才告警,此時就要使用 prometheus alerting rule 的 for 關鍵字,或者夜鶯中的持續時長的配置,表示在一個時間范圍內多次執行,每次都觸發了才告警。

像上例觸發了3個告警事件,如果后面繼續周期性使用promql查詢查不到數據了,就說明最新的mem_available_percent數據不再小于60,即告警恢復。

邏輯/集合運算符

相關運算符有三個:and、or、unless 用于 instant-vector 之間的運算。首先來解釋一下各個運算符的行為。

and

vector1 and vector2,其結果是一個由vector1的元素組成的向量,對于這些元素,vector2中存在著完全匹配的標簽集,其他元素被刪除。metric的名稱和值從左邊的向量轉移過來。用于什么場景?先經過 vector1 做過濾得到一批監控數據,可能里邊有一些是不想要的,可以用 and 操作符,再加一個條件,用另一個 metric 的值做一些二次過濾。舉例:

disk_used_percent{app="clickhouse"} > 70
and
disk_total{app="clickhouse"}/1024/1024/1024 < 500

磁盤利用率大于70%就告警,對于盤不大的情況是適用的,如果盤太大,比如16T一塊盤,使用率70%還有非常大的余量,所以這里我們使用and附加一個條件,限制一下disk_total,即磁盤總大小,磁盤總大小小于500GB,才適用磁盤利用率大于70%這個規則。

or

vector1 or vector2,其結果是一個向量,包含vector1的所有原始元素(標簽集+值)以及vector2中所有在vector1中沒有匹配標簽集的元素。

舉一個例子,比如系統負載,有最近1分鐘、最近5分鐘、最近15分鐘的負載,需求是:最近1分鐘的負載大于8或者最近5分鐘的負載大于8,就告警,promql寫法:

system_load1{app="clickhouse"} > 8
or
system_load5{app="clickhouse"} > 8

unless

vector1 unless vector2,結果是一個由vector1中的元素組成的向量,在vector2中沒有完全匹配標簽集的元素,兩個vector中的所有匹配元素都被刪除。姑且可以理解為一個減法,vector1- vector2。

舉個例子,還是磁盤利用率的問題,對于超過1個T的大盤,剩余量小于300G就告警,promql
怎么寫?

disk_free{app="clickhouse"}/1024/1024/1024 < 300
unless
disk_total{app="clickhouse"}/1024/1024/1024 < 1024

使用 unless 排除掉小于1個T的盤,剩下的就只剩大于1個T的大盤了,效果達成。

向量匹配

向量之間的操作試圖在右側的向量中為左側向量的每個條目找到一個匹配的元素,匹配行為分為:one-to-one、many-to-one、one-to-many。

on 和 ignoring

上面演示 and、or、unless 的例子,兩個vector的標簽集都是一樣的,那如果有些標簽略微有些差異,可以使用 on 和 ignoring 關鍵字來限制用于做匹配的標簽集。舉例:

mysql_slave_status_slave_sql_running == 0
and ON (instance)
mysql_slave_status_master_server_id > 0

這個promql想表達的意思是如果這個mysql實例是個slave(master_server_id>0),則檢查其slave_sql_running的值,如果slave_sql_running==0表示slave sql線程沒有在運行。

但是mysql_slave_status_slave_sql_running和mysql_slave_status_master_server_id這兩個metric的標簽可能并非完全一致,不過好在二者都有個instance標簽,且相同instance標簽的數據從語義上來看就表示一個實例的多個指標數據,那就可以用on關鍵字,指定只使用instance
標簽做匹配,忽略其他標簽。

與on相反的是ignoring關鍵字,顧名思義,ignoring是忽略掉某些標簽,用剩下的標簽來做匹配。我們拿 Prometheus 文檔中的例子來說明:

## example series
method_coderate5m{method="get", code="500"} 24
method_coderate5m{method="get", code="404"} 30
method_coderate5m{method="put", code="501"} 3
method_coderate5m{method="post", code="500"} 6

method_coderate5m{method="post", code="404"} 21
methodrate5m{method="get"} 600
methodrate5m{method="del"} 34
methodrate5m{method="post"} 120
## promql
method_coderate5m{code="500"}
/ ignoring(code)
methodrate5m
## result
{method="get"} 0.04 // 24 / 600
{method="post"} 0.05 // 6 / 120

group_left 和 group_right

這兩個關鍵詞用于 one-to-many 和 many-to-one 的匹配場景,left、right 指向高基數的那一
側的 vector。還是拿上面的 method_coderate5m 和
methodrate5m 這倆指標來做例子,使用 group_left 的 promql 和結果如下:

## promql
method_coderate5m
/ ignoring(code) group_left
methodrate5m
## result
{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

比如針對 method=“get” 的條目,右側的vector中只有一個記錄,但是左側的vector中有兩個
記錄,所以高基數的一側是左側,故而使用 group_left。
另外舉一個例子,說明 group_left group_right 的一個常見用法,比如我們使用 kube-statemetrics 來采集 Kubernetes 各個對象的指標數據,其中針對 pod 有個指標是
kube_pod_labels,會把 pod 的一些信息放到這個指標的標簽里,指標值是1,相當于一個元信
息,比如:

kube_pod_labels{
[...]
  label_name="frontdoor",
  label_version="1.0.1",
  label_team="blue"
  namespace="default",
  pod="frontdoor-xxxxxxxxx-xxxxxx",
} = 1

假設某個 Pod 是接入層的,統計了很多 HTTP 請求相關的指標,我們想統計 5xx 的請求數量,希望能按 Pod 的 version 畫一個餅圖。這里有個難點:接入層這個 Pod 沒有 version 標簽,version 信息只是出現在 kube_pod_labels 中,如何讓二者聯動呢?上答案:

sum(
rate(http_request_count{code=~"^(?:5..)$"}[5m])) by (pod)
*
on (pod) group_left(label_version) kube_pod_labels

我們來掰開揉碎這個 promql 看一下具體的意思,乘號前面的部分,是一個典型的統計每秒5xx 數量的語法,group by pod。

然后我們乘以 kube_pod_labels,這個值是1,所以對整體數值沒有影響,而kube_pod_labels 有多個標簽,而且和sum語句的結果vector的標簽不一致,所以通過on(pod) 的語法指定只是按照pod標簽來做對應關系。

最后,利用 group_left(label_version) 把 label_version 附加到了結果向量中,高基數的部分顯然是sum的部分,所以是group_left而非group_right。

聚合運算

針對單個指標的多個 series,比如100臺機器的 mem_available_percent,可能會有一些聚合需求,比如想查看這100臺機器的平均內存可用率,或者排個序,取數值最小的10臺。這種需求使用promql內置的聚合函數來做。
? sum (calculate sum over dimensions)
? min (select minimum over dimensions)
? max (select maximum over dimensions)
? avg (calculate the average over dimensions)
? group (all values in the resulting vector are 1)
? stddev (calculate population standard deviation over dimensions)
? stdvar (calculate population standard variance over dimensions)
? count (count number of elements in the vector)
? count_values (count number of elements with the same value)
? bottomk (smallest k elements by sample value)
? topk (largest k elements by sample value)
? quantile (calculate φ-quantile (0 ≤ φ ≤ 1) over dimensions)

比如取平均值和取最小的2個可用率數據,其對應的 promql 如下:

avg(mem_available_percent{app="clickhouse"})
bottomk(2, mem_available_percent{app="clickhouse"})

另外,我們有時會有分組統計的需求,比如我想分別統計clickhouse和canal的機器的內存可用率,可以使用by關鍵字指定分組統計的維度(與by相反的是without):

avg(mem_available_percent{app=~"clickhouse|canal"}) by (app)

函數

Prometheus 函數非常多,具體文檔參考:https://prometheus.io/docs/prometheus/latest/querying/functions/ 這一節我們舉例說明一些常用的函數。

absent_over_time

接收一個 range-vector,如果range-vector是空,則返回1,表示absent,如果range-vector有內容,則什么都不返回。
這個特性在生產環境下可以用作nodata告警,比如:

absent_over_time(system_load_norm_1{ident="tt-fc-dev02.nj"}[5m])

這個promql表示,tt-fc-dev02.nj 這個機器在最近5m內如果上報過system_load_norm_1指標,即 tt-fc-dev02.nj 機器存活,則什么都不返回,如果機器掛了,不再上報監控數據了,即指標在最近5m內不存在了,即可判斷機器失聯。

這種方法有個弊端,就是得把指標的所有標簽都寫上,比如我們的需求可能是,100臺機器,任何一臺失聯了就告警,想當然的我們可能會這么寫:

absent_over_time(system_load_norm_1[5m])

很遺憾,這個結果不符合預期,只要任一臺機器有在上報監控數據,這個promql就返回空,即使已經有99臺機器掛了,還剩最后一臺機器在上報監控數據,這個promql也仍然返回空。

所以實際上,如果我們想要對100臺機器使用absent_over_time做失聯告警,就要配置100條告警規則,每個規則里的promql都要把機器標識信息寫上。

對于拉模式的監控系統,比如 Prometheus,很容易判斷機器失聯,因為 pull 不到數據
了,就知道 target 掛了,通過 up 指標就可以告警;對于推模式的監控系統,比如 OpenFalcon、Datadog、Nightingale,就不好搞了。所以夜鶯的告警規則里專門做了一個機器
告警類型,用于機器失聯告警。

increase

這個函數很常用,但是其計算結果可能會出乎意料,這一節詳細講解,打消各位的疑問。字面意思上,表示求取一個增量,接收一個 range-vector,range-vector 顯然是會返回多個value+timestamp 的組合,我們直觀理解就是,直接把時間范圍內最后一個值減去第一個值,不就可以得到增量了嗎?非也!如下圖:

95631224-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
這個圖上的一些關鍵信息,我們摘錄出文本,具體如下:

promql: net_bytes_recv{interface="eth0"}[1m] @ 1661570908
965304237246 @1661570850
965307953982 @1661570860
965311949925 @1661570870
965315732812 @1661570880
965319998347 @1661570890
965323899880 @1661570900

promql: increase(net_bytes_recv{interface="eth0"}[1m]) @1661570909
23595160.8

監控數據是10秒上報一次,所以雖然兩次 promql 查詢時間不同,一次是 1661570908,一次是 1661570909,但是所查詢的原始數據內容是一樣的,就是 1661570850~1661570900 這幾個時間點對應的數據。

直觀上理解,在這幾個時間點對應的數據上求取 increase,無非就是最后一個值減去第一個值,即965323899880-965304237246=19662634,很遺憾,實際結果是23595160.8,差別有點大,顯然這個直觀理解的算法是錯的。
實際上,increase 這個 promql 發起請求的時間是1661570909,時間范圍是[1m],相當于告訴Prometheus,我要查詢1661570849(1661570909-60)~1661570909之間的 increase數值。但是原始監控數據并沒有 1661570849、1661570909 這兩個時刻的數值,怎么辦呢?Prometheus只能基于現有的數據做外推,即使用最后一個點的數值減去第一個點的數值,得到的結果除以時間差,再乘以60,即:

(965323899880.0-965304237246.0)/(1661570900.0-1661570850.0)*60=
23595160.8

上例中,我的測試數據是沒有缺失數據點的,如果有缺失數據點的情況,數據外推會更為
復雜,具體可以參考這篇文章:https://mp.weixin.qq.com/s/9aiqrtLTnzysV9olMx-rzA

rate

趁熱打鐵,說一下 rate 函數,increase 函數是求取的時間段內的增量,而且有數據外推,rate函數則求取的每秒變化率,也有數據外推的邏輯,相當于 increase 的結果除以 range-vector的時間段的大小,就是 rate 的值。我們用如下 promql 做驗證:

rate(net_bytes_recv{interface="eth0"}[1m])
== bool
increase(net_bytes_recv{interface="eth0"}[1m])/60.0

這里 == 后面跟了一個 bool 修飾符,表示希望返回一個 bool 值,如果是 true 就會返回 1,如
果是 false 就返回 0,我們觀察結果會發現,這個表達式永遠都會返回 1,即等號前后的兩個
promql 語義上是相同的。

irate

rate 函數求取的變化率,相對平滑,因為是拿時間范圍內的最后一個值和第一個值做數據外推,一些毛刺現象就會被平滑掉,如果想要得到更敏感的數據,可以使用 irate 函數。irate 是拿時間范圍內的最后兩個值來做計算,變化就會更劇烈,我們還是拿網卡入向流量這個指標來做個對比:

95850bfe-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
藍色的更變化更劇烈的線是 irate 函數計算的,紫色的相對平滑的線是 rate 函數計算得到的。

histogram_quantile

要了解 histogram_quantile 函數的用法,首先得了解 Histogram 類型的數據。Histogram 翻譯過來是柱狀圖,設計這個數據類型,是為了描述響應延時的情況。

比如接口:/api/v1/query,如何度量這個接口的健康狀況?最核心有兩個指標,一個是成功率,一個是延遲,成功率的計算代價比較小,只需要為每個請求指標打上 statuscode 的標簽即可,然后可以求取非 5xx 非 4xx 的請求占比,即可得到成功的數量,除以總量就是成功率。

而對于延遲,如果只是求取平均延遲,代價也比較小,只要把請求總量做成一個 Counter 指標,把耗時總量做成一個 Counter 指標即可。但是,平均響應時間有時并不能很好的反應長尾問題,比如最近1分鐘有1萬個請求,大部分請求都是1秒內返回,有200個請求是10秒返回,平均響應時間是:1.18秒,看起來還不錯,導致我們忽略了這200個長尾請求,而這200個長尾請求,可能恰好是暴露問題的200個請求。

所以在看延遲數據時,我們通常會用分位值,比如99分位,90分位,50分位,所謂的分位值,就是把一段時間內的所有延遲數據從小到大排序,99分位就是看第99%位置的那個值的大小。還是上面的例子,平均響應時間是1.18秒,但是99分位時間是10秒,相差巨大,更容易暴露問題。這里所謂的99分位延遲10秒,可以理解為,99%的請求都在10秒內返回。

從監控系統角度,如何來存儲和計算出99分位值呢?如果每分鐘有1億個請求,難道真的要在監控系統中存儲這1億個請求,然后排序,然后求取分位值?那這個代價就太大了。監控數據是采樣數據,對準確性要求沒有那么的高,有沒有什么辦法可以降低這個代價呢?這就是Prometheus Histogram 的設計初衷了。

Histogram 類型,是把延遲數據分到多個桶里,比如下面的例子,我們查詢一個bucket指標看看效果,雖然這個指標的桶劃分不是很合理,也可以說明問題:

959fa658-2b27-11f0-9310-92fbcf53809c.png

在這里插入圖片描述
binlog_consumer_latency_seconds_bucket 這個指標,有一個非常非常重要的標簽叫 le,表示桶上界,上面的例子就表示,binlog的consume延遲數據分成了6個桶,分別統計了每個桶的總的consume次數:

延遲小于 0.01 秒的次數: 1
延遲小于 0.1 秒的次數: 321
延遲小于 1 秒的次數: 14588
延遲小于 5 秒的次數: 56826
延遲小于 10 秒的次數: 56843
延遲小于 30 秒的次數: 56881
延遲小于 60 秒的次數: 56975
所有consume總數 : 120396

假設我們統計50分位,那就是120396*0.5=60198.0,落到了 le=“+Inf” 這個桶里了,所以我們斷定50分位的值一定是大于60秒的,當然,因為這個桶劃分不是很合理,導致,90分位、99分位,定然也是在 le=“+Inf” 桶里,即值一定是大于60秒的,因為 le=“+Inf” 這個桶沒有上界,導致我們無法區分這幾個分位值。

下面我們假設一個指標及其數據,做一個算法演示,假設指標名是http_request_duration_seconds_bucket ,其各個 bucket 的值如下:

http_request_duration_seconds_bucket{job="n9e-proxy", le="0.1"} 500
http_request_duration_seconds_bucket{job="n9e-proxy", le="1"} 700
http_request_duration_seconds_bucket{job="n9e-proxy", le="10"} 850
http_request_duration_seconds_bucket{job="n9e-proxy", le="20"} 1000
http_request_duration_seconds_bucket{job="n9e-proxy", le="+Inf"} 1000

根據這個數據,我們可以計算出落在各個延遲區間的請求數量,如下:

0 ~ 0.1 : 500
0.1 ~ 1 : 200
1 ~ 10 : 150
10 ~ 20 : 150
20 ~ +Inf : 0

總共有1000個請求,我們來計算其90分位的值,即1000*0.9=900,第900個請求,顯然,第900個請求落在了1020這個區間,即90分位的延遲是10秒20秒,那具體是多少?其實是無法知曉的,不過 Prometheus 的 histogram_quantile 有個估計算法,它假設落在各個 bucket
的數據是均勻分布的,即10~20這個區間的150個請求,延遲最小的那個請求是10s,延遲最大的那個請求是20秒,總的第900個請求,就是這個區間的第50個請求,其延遲數據大概是:

(20-10)*(50/150)+10=13s

這是假設數據是均勻分布在各個桶的,假設10~20那個桶的150個請求,最大延遲的那個請求,其延遲數據是11秒,而這里算出13秒,顯然與現實不符,不符也沒辦法,這本來就是個預估值,知道大概數量級就可以了,還是那句話,監控數據是采樣數據,這么計算雖然不是那么準確,但是成本低。

實際上,我們基于某個指標的歷史所有數據計算分位值,意義不大,通常我們是基于最近一段時間的增量數據來計算,比如基于10分鐘區間的增量數據計算,就可以較為方便的知道,當前這個10分鐘的延遲是多少,上一個10分鐘的延遲是多少。histogram_quantile 接收兩個參數,第一個是分位標量,第二個 instant-vector(這個vector的標簽中一定要有 le 標簽),舉例:

histogram_quantile(0.9, rate(http_request_duration_seconds_bucket[10m]))

上面的例子,是會對每個請求分別做計算,假設有兩個模塊:n9e-proxy、n9e-webapi,都統計了 http_request_duration_seconds_bucket ,我們可能希望以模塊為顆粒度,分別計算每個模塊的90分位延遲,寫法是:

histogram_quantile(
0.9,
sum by (job, le) (rate(http_request_duration_seconds_bucket[10m]))
)

注意,這里通過job標簽來區分模塊,le是計算histogram_quantile必須的,所以也要放到sum by后面,如果我們要計算全部數據的90分位值呢(雖然這大概率是個偽需求)?

histogram_quantile(
0.9,
sum by (le) (rate(http_request_duration_seconds_bucket[10m]))
)

針對分位值的計算,已經闡述清楚了,但是分位值的計算是個挺重的查詢,可能會把后端時序庫打爆,所以很多公司可能在業務埋點SDK中不提供histogram這種方式,只提供summary方式。

所謂的summary,也是prometheus的一種埋點數據類型,summary也可以計算90分位、99分位的值,但是這個值不是通過promql在服務端計算的,而是在應用的內存里,在SDK層面計算的,即客戶端把這個分位值算好,再上報給服務端,服務端就無需通過histogram_quantile
這么重的函數做計算了,而是直接查看就好。

但是,既然是在客戶端SDK層面計算,就會產生局限,這些分位值只能是實例級別(或者說進程
級別,因為SDK是在應用進程里運行的)的分位值,這個是否個問題?

筆者看來,這是個問題,但是這個問題不是特別嚴重,如果要求全局的90分位值,可以把所有實例的90分位值取個平均,雖然不是那么準確,也湊合能用。而實際上,對于一個服務部署多個實例的場景,通常這多個實例是負載均衡的,查看其中一個實例的分位值和查看總體的分位值理論上差不太多。而且,如果某個機器有問題,比如某個機器磁盤故障,導致部署在上面的實例異常,延遲變高,其他實例都是正常的,全局查看延遲數據的時候,每個實例是一條曲線,那個故障的機器,對應的曲線應該是恰好嚴重偏離其他曲線,正好可以借機知道具體是哪個實例/機器出了問題。

_over_time

這類聚合函數和聚合運算章節提供的sum、avg等聚合運算符非常像,容易混淆,著重做一個說明,比如avg,參數是instant-vector,是在同一時刻,對多個series的多個值求平均,而avg_over_time,參數是 range-vector,是根據指定的時間范圍,求取時間范圍內的多個值的
平均。

比如 avg_over_time(mem_available_percent{ident=“10.3.4.5”}[1m]) 是取10.3.4.5這個機器的內存可用率,取其最近1分鐘內的多個值(如果10秒上報一次,1分鐘內有6個值),求平均。

更多函數就不過多介紹了,相對容易理解,參考 Prometheus 官方文檔即可。最后擴展介紹一個 MetricsQL(MetricsQL 是 VictoriaMetrics 提供的一種查詢語言,兼容 PromQL 并對其做了增強,如果你的存儲是 VictoriaMetrics,則可以使用這些擴展函數) 中的擴展函數。

count_gt_over_time

假設原始需求:某個指標( 假設指標名字是 interface_status )每分鐘上報一次,如果 5 分鐘
內有 3 次大于 10,就報警。使用 PromQL 比較難寫,使用 MetricsQL 就非常簡單:

count_gt_over_time(interface_status[5m], 10) >= 3

看到這個寫法,基本能直觀理解其含義了, count_gt_over_time(series_selector[d], gt) 函數有兩個參數,一個是 range-vector,一個是標量 gt,表示在 range-vector 中大于 gt 的個數,如果大于等于 3,就報警。除了 count_gt_over_time 函數之外,還有count_le_over_time、count_ne_over_time、count_eq_over_time 道理相同。

小結

上面的知識點是 PromQL 的常規知識,盡量融入了一些生產實踐的場景,當然,PromQL 還有更多函數沒有介紹,大家可以閱讀其文檔學習。

鏈接:https://blog.csdn.net/weixin_38908201/article/details/136302699

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 監控系統
    +關注

    關注

    21

    文章

    4030

    瀏覽量

    181175
  • 時序
    +關注

    關注

    5

    文章

    397

    瀏覽量

    37772
  • 數據類型
    +關注

    關注

    0

    文章

    237

    瀏覽量

    13819
  • Prometheus
    +關注

    關注

    0

    文章

    29

    瀏覽量

    1839

原文標題:運維/DevOps 必學!PromQL 入門指南,輕松玩轉 Prometheus

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦
    熱點推薦

    史上最全Python數據類型詳解

    數據類型是每個編程語言必不可少的基礎知識,也是必須要掌握的技能,很多人知識簡單的進行理解,并沒有很系統的進行知識的梳理,以下是對python語言的全部數據類型詳細匯總:Python中可以自定義
    發表于 06-15 14:57

    vhdl數據類型

    VHDL中的標識符可以是常數、變量、信號、端口、子程序或參數的名字。VHDL中的數據類型可以分成四大類: 標量型(SCALAR TYPE):屬單元素的最基本的數據類型,通常用于描述一個單值數據對象
    發表于 03-30 15:59 ?11次下載

    51單片機學習筆記(9)——C51的基本數據類型和擴充數據類型

    基本數據類型:擴充數據類型
    發表于 11-14 13:36 ?1次下載
    51單片機學習筆記(9)——C51的基本<b class='flag-5'>數據類型</b>和擴充<b class='flag-5'>數據類型</b>

    labview數據類型簡介

    labview數據類型簡介
    發表于 01-11 09:42 ?6次下載

    Struct結構數據類型

    Struct類型是一種由多個不同數據類型元素組成的數據結構,其元素可以是基本數據類型,也可以是Struct、數組等復雜數據類型以及PLC
    的頭像 發表于 07-25 17:02 ?3332次閱讀

    結構數據類型(Struct)及應用案例

    Struct數據類型使用非常靈活,隨時可以使用,但是相對于PLC數據類型 (UDT) 有以下缺點,所以建議需要使用Struct類型時,可以使用PLC數據類型(UDT)代替。
    的頭像 發表于 07-27 16:10 ?2168次閱讀

    什么是數據類型轉換

    常用的3種數據類型:1、Python數據類型第一種:字符串(str)。 2、Python數據類型第二種:整數(int)。 3、Python數據類型第三種:浮點數(float)。
    的頭像 發表于 02-23 15:21 ?1954次閱讀

    定義數據類型

    在運算之前我們必須首先定義出數據類型,定義出腳本支持的數據類型,這是運算的基礎。 這一小節我們將定義出數據類型,在這里我們暫時定義四個數據類型:
    的頭像 發表于 03-03 10:10 ?1215次閱讀

    PLC數據類型

    基本數據類型 ? ? ? 基本數據類型? ? 包括位、位序列、整數、浮點數、日期時間。(常見的) 1、位 字節 字 雙字 2、數據類型 3 浮點數 實(或浮點)數以 32 位單精度數 (Real
    發表于 04-17 15:49 ?0次下載
    PLC<b class='flag-5'>數據類型</b>

    基本數據類型分享

    基本數據類型 基本數據類型:包括位、位序列、整數、浮點數、日期時間。此外字符也屬于基本數據類型,請參見文檔String與WString。 1.位和位序列 2.整數數據類型 3.浮點型實
    的頭像 發表于 06-13 14:14 ?1.1w次閱讀
    基本<b class='flag-5'>數據類型</b>分享

    ARRAY 數據類型的變量

    要求 全局數據塊已打開。 操作步驟 要聲明一個 ARRAY 數據類型的變量,請按以下步驟操作: 在“名稱”(Name) 列中,輸入變量的名稱。 在“數據類型”列中輸入“Array”數據類型
    的頭像 發表于 07-06 11:08 ?1597次閱讀

    F型PLC數據類型與標準PLC數據類型(UDT)之間的差別在哪?

    可以像使用標準 PLC 數據類型 (UDT) 那樣,聲明和使用 F 型 PLC 數據類型 (UDT) 。可以在安全程序中以及標準用戶程序中使用 F 型 PLC 數據類型 (UDT) 。
    的頭像 發表于 08-27 09:54 ?1453次閱讀
    F型PLC<b class='flag-5'>數據類型</b>與標準PLC<b class='flag-5'>數據類型</b>(UDT)之間的差別在哪?

    Redis的數據類型有哪些

    Redis的數據類型有哪些?有五種常用數據類型:String、Hash、Set、List、SortedSet。以及三種特殊的數據類型:Bitmap、HyperLogLog、Geospatial
    的頭像 發表于 10-09 10:51 ?998次閱讀

    oracle的數據類型有哪些

    Oracle數據庫中有許多數據類型可供選擇,每種數據類型都有其各自的特點和適用場景。下面是對Oracle數據庫中最常用的數據類型的詳盡說明,
    的頭像 發表于 12-05 16:45 ?2963次閱讀

    plc數據類型怎么理解和應用

    PLC(可編程邏輯控制器)是一種工業自動化設備,用于控制機械和工業過程。在PLC編程中,數據類型是非常重要的概念,因為它決定了程序中數據的存儲和處理方式。正確理解和應用PLC數據類型是編寫有效、可靠
    的頭像 發表于 12-19 11:39 ?5313次閱讀