PromQL 查詢之 Rate 函數(shù)的使用
通常來(lái)說(shuō)直接繪制一個(gè)原始的 Counter 類型的指標(biāo)數(shù)據(jù)用處不大,因?yàn)樗鼈儠?huì)一直增加,一般來(lái)說(shuō)是不會(huì)去直接關(guān)心這個(gè)數(shù)值的,因?yàn)?Counter 一旦重置,總計(jì)數(shù)就沒有意義了,比如我們直接執(zhí)行下面的查詢語(yǔ)句:
- demo_api_request_duration_seconds_count{job="demo"}
可以得到下圖所示的圖形:
可以看到所有的都是不斷增長(zhǎng)的,一般來(lái)說(shuō)我們更想要知道的是 Counter 指標(biāo)的變化率,PromQL 提供了不同的函數(shù)來(lái)計(jì)算變化率。
rate
用于計(jì)算變化率的最常見函數(shù)是 rate(),rate() 函數(shù)用于計(jì)算在指定時(shí)間范圍內(nèi)計(jì)數(shù)器每秒增加量的平均值。因?yàn)槭怯?jì)算一個(gè)時(shí)間范圍內(nèi)的平均值,所以我們需要在序列選擇器之后添加一個(gè)范圍選擇器。
例如我們要計(jì)算 demo_api_request_duration_seconds_count 在最近五分鐘內(nèi)的每秒平均變化率,則可以使用下面的查詢語(yǔ)句:
- rate(demo_api_request_duration_seconds_count[5m])
可以得到如下所示的圖形:
現(xiàn)在繪制的圖形看起來(lái)顯然更加有意義了,進(jìn)行 rate 計(jì)算的時(shí)候是選擇指定時(shí)間范圍下的第一和最后一個(gè)樣本進(jìn)行計(jì)算,下圖是表示瞬時(shí)計(jì)算的計(jì)算方式:
往往我們需要的是繪制一個(gè)圖形,那么就需要進(jìn)行區(qū)間查詢,指定一個(gè)時(shí)間范圍內(nèi)進(jìn)行多次計(jì)算,將結(jié)果串聯(lián)起來(lái)形成一個(gè)圖形:
對(duì)于 rate() 和相關(guān)函數(shù)有幾個(gè)需要說(shuō)明的:
- 當(dāng)被抓取指標(biāo)進(jìn)的程重啟時(shí),Counter 指標(biāo)可能會(huì)重置為 0,但 rate() 函數(shù)會(huì)自動(dòng)處理這個(gè)問(wèn)題,它會(huì)假設(shè) Counter 指標(biāo)的值只要是減少了就認(rèn)為是被重置了,然后它可以調(diào)整后續(xù)的樣本,例如,如果時(shí)間序列的值為[5,10,4,6],則將其視為[5,10,14,16]。
- 變化率是從指定的時(shí)間范圍下包含的樣本進(jìn)行計(jì)算的,需要注意的是這個(gè)時(shí)間窗口的邊界并不一定就是一個(gè)樣本數(shù)據(jù),可能會(huì)不完全對(duì)齊,所以,即使對(duì)于每次都是增加整數(shù)的 Counter,也可能計(jì)算結(jié)果是非整數(shù)。
另外我們需要注意當(dāng)把 rate() 與一個(gè)聚合運(yùn)算符(例如 sum())或一個(gè)隨時(shí)間聚合的函數(shù)(任何以 _over_time 結(jié)尾的函數(shù))結(jié)合起來(lái)使用時(shí),總是先取用 rate() 函數(shù),然后再進(jìn)行聚合,否則,當(dāng)你的目標(biāo)重新啟動(dòng)時(shí),rate() 函數(shù)無(wú)法檢測(cè)到 Counter 的重置。
注意:rate() 函數(shù)需要在指定窗口下至少有兩個(gè)樣本才能計(jì)算輸出。一般來(lái)說(shuō),比較好的做法是選擇范圍窗口大小至少是抓取間隔的4倍,這樣即使在遇到窗口對(duì)齊或抓取故障時(shí)也有可以使用的樣本進(jìn)行計(jì)算,例如,對(duì)于 1 分鐘的抓取間隔,你可以使用 4 分鐘的 Rate 計(jì)算,但是通常將其四舍五入為 5 分鐘。所以如果使用 query_range 區(qū)間查詢,例如在繪圖中,那么范圍應(yīng)該至少是步長(zhǎng)的大小,否則會(huì)丟失一些數(shù)據(jù)。
irate
由于使用 rate 或者 increase 函數(shù)去計(jì)算樣本的平均增長(zhǎng)速率,容易陷入長(zhǎng)尾問(wèn)題當(dāng)中,其無(wú)法反應(yīng)在時(shí)間窗口內(nèi)樣本數(shù)據(jù)的突發(fā)變化。
例如,對(duì)于主機(jī)而言在 2 分鐘的時(shí)間窗口內(nèi),可能在某一個(gè)由于訪問(wèn)量或者其它問(wèn)題導(dǎo)致 CPU 占用 100%的情況,但是通過(guò)計(jì)算在時(shí)間窗口內(nèi)的平均增長(zhǎng)率卻無(wú)法反應(yīng)出該問(wèn)題。
為了解決該問(wèn)題,PromQL 提供了另外一個(gè)靈敏度更高的函數(shù)irate(v range-vector)。irate 同樣用于計(jì)算區(qū)間向量的計(jì)算率,但是其反應(yīng)出的是瞬時(shí)增長(zhǎng)率。
irate 函數(shù)是通過(guò)區(qū)間向量中最后兩個(gè)樣本數(shù)據(jù)來(lái)計(jì)算區(qū)間向量的增長(zhǎng)速率。這種方式可以避免在時(shí)間窗口范圍內(nèi)的長(zhǎng)尾問(wèn)題,并且體現(xiàn)出更好的靈敏度,通過(guò) irate 函數(shù)繪制的圖標(biāo)能夠更好的反應(yīng)樣本數(shù)據(jù)的瞬時(shí)變化狀態(tài)。那既然是使用最后兩個(gè)點(diǎn)計(jì)算,那為什么還要指定類似于 [1m] 的時(shí)間范圍呢?這個(gè) [1m] 不是用來(lái)計(jì)算的,irate 在計(jì)算的時(shí)候會(huì)最多向前在 [1m] 范圍內(nèi)找點(diǎn),如果超過(guò) [1m] 沒有找到數(shù)據(jù)點(diǎn),這個(gè)點(diǎn)的計(jì)算就放棄了。
由于 rate() 提供了更平滑的結(jié)果,因此在長(zhǎng)期趨勢(shì)分析或者告警中更推薦使用 rate 函數(shù),因?yàn)楫?dāng)速率只出現(xiàn)一個(gè)短暫的峰值時(shí),不應(yīng)該觸發(fā)該報(bào)警。
使用 irate() 函數(shù)上面的表達(dá)式會(huì)出現(xiàn)一些短暫下降的圖形:
除了計(jì)算每秒速率,你還可以使用 increase() 函數(shù)查詢指定時(shí)間范圍內(nèi)的總增量,它基本上相當(dāng)于速率乘以時(shí)間范圍選擇器中的秒數(shù):
- increase(demo_api_request_duration_seconds_count{job="demo"}[1h])
比如上面表達(dá)式的結(jié)果和使用 rate() 函數(shù)計(jì)算的結(jié)果整體圖形趨勢(shì)都是一樣的,只是 Y 軸的數(shù)據(jù)不一樣而已,一個(gè)表示數(shù)量,一個(gè)表示百分比。rate()、irate() 和 increase() 函數(shù)只能輸出非負(fù)值的結(jié)果,對(duì)于跟蹤一個(gè)可以上升或下降的值的指標(biāo)(如溫度、內(nèi)存或磁盤空間),可以使用 delta() 和 deriv() 函數(shù)來(lái)代替。
deriv() 函數(shù)可以計(jì)算一個(gè)區(qū)間向量中各個(gè)時(shí)間序列二階導(dǎo)數(shù),使用簡(jiǎn)單線性回歸,deriv(v range-vector) 的參數(shù)是一個(gè)區(qū)間向量,返回一個(gè)瞬時(shí)向量,這個(gè)函數(shù)一般只用在 Gauge 類型的時(shí)間序列上。例如,要計(jì)算在 15 分鐘的窗口下,每秒鐘磁盤使用量上升或下降了多少:
還有另外一個(gè) predict_linear() 函數(shù)可以預(yù)測(cè)一個(gè) Gauge 類型的指標(biāo)在未來(lái)指定一段時(shí)間內(nèi)的值,例如我們可以根據(jù)過(guò)去 15 分鐘的變化情況,來(lái)預(yù)測(cè)一個(gè)小時(shí)后的磁盤使用量是多少,可以用如下所示的表達(dá)式來(lái)查詢:
- predict_linear(demo_disk_usage_bytes{job="demo"}[15m], 3600)
這個(gè)函數(shù)可以用于報(bào)警,告訴我們磁盤是否會(huì)在幾個(gè)小時(shí)候內(nèi)用完。