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