詳解Prometheus range query中的step引數
Prometheus有兩種query:instant query、range query。本文要講的就是range query中的step引數。
range query是非常常見的一種query,看看它有哪些引數:
-
query=<string>
: PromQL表示式。 -
start=<rfc3339 | unix_timestamp>
: 時間範圍的開始。 -
end=<rfc3339 | unix_timestamp>
: 時間範圍的結束。 -
step=<duration | float>
: 查詢解析度(query resolution)。 -
timeout=<duration>
在Prometheus expression browser裡看到的是這樣的:
注意到上圖中的Res框裡沒有給值,沒有給的話Prometheus會自動給一個值,這個值在圖示右上角可以看到。
step對於查詢結果的影響
Prometheues在對PromQL表示式求值的邏輯是這樣的(詳見這個issue裡的回答):
- 對於[start, end]時間區間,從start開始,以step為長度,把時間區間分成若干段
- 對每個段進行求值
舉例:start=10,end=20,step=2
,那麼就會有ts=10,ts=12,ts=14,ts=16,ts=18,ts=20
6段,然後為這6個段進行求值。求值方式視乎表示式中Time series selector的型別而定。
PromQL有兩種Time series selector:instant vector selector和range vector selector。下面將分別講解:
Instant vector selector
形如下面的就是Instant vector selector,x
是metric的名字。
x
Prometheus在對每段Instant vector selector求值的邏輯是這樣的:
- 從該段的timestamp(含)往前找,取第一個找到的data point的值。如果有一個data point的timestamp==該段的timestamp,則直接使用該data point。
- 如果該段timestamp往前的5分鐘範圍內沒有找到任何data point,則該段無值。
下面這張圖解釋了上面邏輯:
圖中的綠點是Prometheus實際儲存的資料,按照時間軸從左到右排列。藍點是根據step引數的求值結果。
當data point間隔比step更大的時候會發生下圖這種情況:
可以看到有兩個段的求值結果來自於同一個data point。
Range vector selector
形如下面的就是Range vector selector,x
是metric的名字,方括號裡的是range duration
。
x[5m]
range vector select返回的是當前timestamp之前的range duration
內的所有data point。range vector是不能直接用做繪圖的,你得用某些function把range vector轉換成instant vector才行,比如rate()。
下圖解釋了是如何對Range vector selector進行分段求值的:
step和rate duration
step
和range duration
是獨立的兩個引數,在某些情況下兩者的值存在某種限制條件,這裡例舉rate()來說明。rate()
的作用是獲得一個range-vector的每秒平均增長率。
如果step=10m
而range duration=5m
,那麼rate
在計算的時候會丟失一半的資料,兩個分段之間的data point有一半沒有被納入計算。前面那張圖就存在資料丟失的情況,有一個data point被漏掉了。
因此在使用rate()
時,range duration
得大於等於step
。
而如果是irate(),這個限制則是range duration
不得大於step
(詳見Brian Brazil的Presentation)。
Grafana中的step引數
在Grafana中並沒有直接提供step
引數,而是這兩個引數:min step
和resolution
(文件在這裡)。min step
故名思義設定的是step
的最小值,那麼resolution
是什麼呢?
大家都知道Grafana都是用來畫圖的,比如下面這張圖Y軸是值,X軸則是時間線,因此在X軸方向的每個畫素都代表了一個timestamp。
resolution
就是用來根據畫素來計算step
的一個引數。下面用6個畫素以及它們的timestamp來說明:
x=1,ts=0; x=2,ts=5; x=3,ts=10; x=4,ts=15; x=5,ts=20; x=6,ts=25
-
resolution=1/1
時,那麼step
就是相鄰畫素所代表的timestamp的差,即5; -
resolution=1/2
時,那麼step
就是相隔1個畫素的兩個畫素的timestamp的差,即10; -
resolution=1/3
時,那麼step
就是相隔2個畫素的兩個畫素的timestamp的差,即15; - 以此類推
而每個畫素所代表的timestamp受兩個因素影響:
- 查詢所定義的時間範圍
- Graph的寬度(單位:畫素)
所以在Grafana發起的查詢中step
引數是動態的。其實這也是很合理的,因為只有這樣才能夠在Graph寬度小的時候繪圖更粗糙(即step
更大),Graph寬度大的時候繪圖更精細(即step
更小,但是不能小於min step
)。實際發起的請求的step
引數你可以在Graph的Query Inspector裡看到:
但是我們之前不說過了rate()
的range duration
不能小於step
嗎?那麼把range duration
給固定值的化就不太好了,怎麼辦呢?你可以使用Grafana提供的內建變數$__interval
,它代表的Grafana就是計算出來的step
的值。比如這樣就能夠將range duration
和step
保持一致了(更多內建變數可以見這裡):
rate(x[$__interval])