1. 程式人生 > 其它 >詳解Prometheus range query中的step引數

詳解Prometheus range query中的step引數

Prometheus有兩種query:instant queryrange 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裡的回答):

  1. 對於[start, end]時間區間,從start開始,以step為長度,把時間區間分成若干段
  2. 對每個段進行求值

舉例:start=10,end=20,step=2,那麼就會有ts=10,ts=12,ts=14,ts=16,ts=18,ts=206段,然後為這6個段進行求值。求值方式視乎表示式中Time series selector的型別而定。

PromQL有兩種Time series selector:instant vector selectorrange 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

steprange duration是獨立的兩個引數,在某些情況下兩者的值存在某種限制條件,這裡例舉rate()來說明。rate()的作用是獲得一個range-vector的每秒平均增長率。

如果step=10mrange duration=5m,那麼rate在計算的時候會丟失一半的資料,兩個分段之間的data point有一半沒有被納入計算。前面那張圖就存在資料丟失的情況,有一個data point被漏掉了。

因此在使用rate()時,range duration得大於等於step

而如果是irate(),這個限制則是range duration不得大於step(詳見Brian Brazil的Presentation)。

Grafana中的step引數

在Grafana中並沒有直接提供step引數,而是這兩個引數:min stepresolution文件在這裡)。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受兩個因素影響:

  1. 查詢所定義的時間範圍
  2. Graph的寬度(單位:畫素)

所以在Grafana發起的查詢中step引數是動態的。其實這也是很合理的,因為只有這樣才能夠在Graph寬度小的時候繪圖更粗糙(即step更大),Graph寬度大的時候繪圖更精細(即step更小,但是不能小於min step)。實際發起的請求的step引數你可以在Graph的Query Inspector裡看到:

但是我們之前不說過了rate()range duration不能小於step嗎?那麼把range duration給固定值的化就不太好了,怎麼辦呢?你可以使用Grafana提供的內建變數$__interval,它代表的Grafana就是計算出來的step的值。比如這樣就能夠將range durationstep保持一致了(更多內建變數可以見這裡):

rate(x[$__interval])