prometheus存儲模型

Prometheus是目前被廣泛使用的容器雲監控系統,其底層實現了一個時序數據庫(tsdb),它擁有強大的數據壓縮和檢索能力,可輕鬆應對每秒百萬級的數據採集。本文基於最新的prometheus 2.12,爲讀者介紹prometheus tsdb存儲模型。

1 時序數據

時序數據,就是按時間順序採集而來的數據,一般表示爲:

identifier -> (t0, v0), (t1, v1), (t2, v2), (t3, v3), ....

identifier是一個時間序列的標示,在prometheus中是以指標名稱+label的形式來表示的,例如:

requests_total{path="/status", method="GET", instance=”10.0.0.1:80”}
requests_total{path="/status", method="POST", instance=”10.0.0.3:80”}
requests_total{path="/", method="GET", instance=”10.0.0.2:80”}

這是我們使用prometheus時能看到的內容,在prometheus內部,它是:

{__name__="requests_total", path="/status", method="GET", instance=”10.0.0.1:80”}
{__name__="requests_total", path="/status", method="POST", instance=”10.0.0.3:80”}
{__name__="requests_total", path="/", method="GET", instance=”10.0.0.2:80”}

那麼prometheus tsdb保存的數據就是標示及其時序數據。
在這裏插入圖片描述
在prometheus中,series代表標示,samples代表採集的時序數據(time,value)

2 WAL

Prometheus採集到的當前數據,存於內存中,如果程序異常關閉,會導致當前的監控數據丟失。爲此Prometheus實現了 wal功能(write ahead log),wal負責將當前內存中的監控數據同步到磁盤。prometheus重啓後,可以讀取wal數據,達到恢復數據,減少損失的目的。

2.1 概念

wal segments:存儲段,在磁盤中的形式是一個帶編號的文件,例如000000, 000001, 000002。每一段默認最大是128MB。
在這裏插入圖片描述
wal page:wal在內存中的緩衝隊列,能存32KB的數據,數據超過buf容量,則寫入segment。
wal record:一段實際的數據,編碼格式如下圖。
在這裏插入圖片描述
wal 有3種record:

  • series
    在這裏插入圖片描述
    類型是1,每個series包含id和label信息。
  • sample(時序數據time,value)
    在這裏插入圖片描述
    類型是2,包含id和時序數據(time,value)
  • tombstone
    在這裏插入圖片描述
    類型是3,包含時序數據id,開始時間,結束時間。
    這3種數據,通過id,彼此關聯。
    record保存在page中,如果record大小超過page容量,會發生flush。
    flush就是把record直接寫入磁盤,發生條件是:
    (1)record本身比page大;
    (2)record大於page的剩餘空間。

wal本身做的事情很清晰,就是對segment讀、寫、創建、刪除。它作爲tsdb的一個組成,和很多功能有聯繫,我們通過下面幾個小結,來全面學習它。

2.2 WAL的數據寫入

在這裏插入圖片描述
從類結構可以看出,wal和Head緊密相關,Head可以看成是當前內存中的數據。
在這裏插入圖片描述
headAppender實現了數據的寫入,它會將內存中的數據寫入wal。Wal Log的作用是將數據寫入page。因爲wal對segment文件大小做出了限制,所以如果寫入的record超過了segment的剩餘空間,wal就會生成一個新的segment。

2.3 Checkpoint

在wal目錄中,經常會看到前綴是checkpoint的目錄。那麼它和wal的關係是什麼?
在這裏插入圖片描述
WAL寫入segment的數據,是未經壓縮過的,所以會比較大。
此外wal的最近3個segment文件,被認爲保存了當前的實時數據。當然,可能由於監控數據量大、採集週期短,segment文件個數迅速膨脹,所以segment文件數量很可能不止3個。
在這裏插入圖片描述
爲了避免segment文件過多,佔用磁盤空間。對最近幾個segment文件,如果其record數據的時間值大於mint,都認爲是當前的實時數據,對這些數據進行壓縮保存,形成新segment文件,保存在chenkpoint.*目錄中。注意:Checkpoint不處理前3個segment文件。

2.4 Truncate

暫存在內存中的數據,一般過2個小時就會寫入磁盤,做永久保存。wal作爲內存數據的備份,當數據保存好後,就完成了使命。
在這裏插入圖片描述
Truncate由tsdb 發起,一般有新的block(數據寫入磁盤)產生時執行。tsdb在執行reload函數過程中調用head. Truncate方法。該方法首先創建checkpoint文件,然後將比較老的segment文件刪除。
在這裏插入圖片描述

2.5 從wal中恢復數據

讓我們先來回顧一下前面的內容:
(1) 內存中的監控數據會實時寫入segment。
(2) 一部分監控數據在內存裏積攢2小時後,寫入磁盤。tsdb會從衆多segment數據中篩選出較新的數據,壓縮保存生成checkpoint,其他的則刪除。 Checkpoint+最近3個segment的數據代表了內存中的當前監控數據。
Prometheus重啓,那麼之前保存在內存中的監控數據,肯定消失了。恢復的辦法,顯而易見就是在啓動時,讀取checkponit+segment的內容。
在這裏插入圖片描述

3 Block

Prometheus收集到的數據,先保存在內存中,一方面可以加快對最近時序數據的查找,另外可以通過批量寫的方式,減少頻繁的磁盤寫入。隨着時間流逝,數據最終會保存到磁盤中。一搬每隔2小時,數據會以block的形式保存在磁盤上。

3.1 Block結構

如果打開prometheus的data目錄,會發現它的結構是這樣的:
在這裏插入圖片描述
類似這種“01DMH”的目錄,就是一個block,從block的生成時間可以看出每2個小時產生一個block,這也印證了內存中的數據保存2小時後會寫一次磁盤。
Block的內容包括:
在這裏插入圖片描述
(1) Chunks,保存了這段時間內的全部時序數據,和wal的segment類似,每個chunk文件都用000001這種形式命名。同樣,chunk文件也不能太大,每個最大512MB,若超過,就生成一個新的chunk文件,這和wal segment邏輯是相似的。
(2) Index,數據的索引,因爲要對歷史數據搜索,使用索引能加速對數據的查找。
(3) Meta,記錄了block的基本信息,是一個很重要的文件,後續的壓縮和刪除都會使用到meta。

cat meta.json 
{
	"ulid": "01DMHXFFFKYK8EMXKEC0VCF826",
	"minTime": 1568253600000,
	"maxTime": 1568260800000,
	"stats": {
		"numSamples": 223656,
		"numSeries": 1902,
		"numChunks": 1902
	},
	"compaction": {
		"level": 1,
		"sources": [
			"01DMHXFFFKYK8EMXKEC0VCF826"
		]
	},
	"version": 1
}
  • uid,block的編號,和目錄同名。
  • minTime、maxTime, Block存儲的數據的時間範圍。
  • stats,block保存的series、samples、chunks數量。一個chunk包含了一段時間的時序數據。
  • Compaction,block的壓縮信息,多個block會壓縮合併成更大的block。

(4)Tombstones,保存對series的刪除記錄。

3.2 Block壓縮

保存在磁盤中的每個block,儼然已經是一個獨立的小數據庫了,每個block都保存了獨立時間範圍的監控數據,並且具備獨立的索引。但是對於時間跨度大的查詢請求,例如要查找一週的數據,需要分別訪問84個block,併合並查詢結果,這樣明顯影響查詢效率。
因此prometheus實現了block的合併機制,就是將現有的block合併成若干個更大的block。如下圖所示,合併前有[1,2,3,4] 4個block,合併時可能1-3合併成新block或3-4合併成新的。
在這裏插入圖片描述

3.3 Block清理

由於本地的磁盤空間有限,所以prometheus提供了磁盤清理功能。
Prometheus的啓動參數“storage.tsdb.retention.time”,定義了block的保存時長,默認保存15天,超過會自動刪除。
它的邏輯是,使用第一個block(已事先排序)的maxTime,與其他block的maxTime比較,如果差值大於retention-time,則這個block之後的所有數據全部刪除。
在這裏插入圖片描述

4 remote write

從前面的章節中可知,Prometheus的數據保存在本地磁盤,受限於磁盤空間,監控數據無法長時間存儲,因此社區提供了可選的remote write功能,將本地的數據,轉入到遠端的外部時序數據庫。
外部數據庫需要實現一個adapter,用來將prometheus的數據格式轉化爲自己的格式。Prometheus社區已經提供了一些主流時序數據庫的remote-write adapter,例如graphite、influex、openstdb。
在這裏插入圖片描述
寫到遠端的數據,是通過wal watcher跟蹤wal,獲得當前的實時數據。數據使用批量寫的模式,先寫到shards中,shards是一種動態隊列集合,它綜合考慮了監控數據的採集量及數據寫入到遠端的數量,動態調節對列的數量。最後調用client向adapter發送數據。

5 總結

最後我們完整的展現Prometheus tsdb的模型,採集的數據首先保存在內存中,同時在wal中也會有一個備份。每過2小時,內存中的數據會保存爲一個block文件,隨着時間的增加,這些block會被合併,最終舊的block被刪除。如果有數據長期存儲的需求,需要配置remote write,remote write會把wal中的數據,寫入到遠程存儲。
參考:
https://www.promlts.com/resources/prometheus-remote-storage
https://www.cnblogs.com/YaoDD/p/11391335.html
https://fabxc.org/tsdb/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章