1. 程式人生 > >Prometheus TSDB分析

Prometheus TSDB分析

命中 高效 內容 結束 調度 tab mes 遠程 evel

Prometheus TSDB分析

概述

Prometheus是著名開源監控項目,其監控任務調度給具體的服務器,該服務器到目標上抓取監控數據,然後保存在本地的TSDB中。自定義強大的PromQL語言查詢實時和歷史時序數據,支持豐富的查詢組合。
Prometheus 1.0版本的TSDB(V2存儲引擎)基於LevelDB,並且使用了和Facebook Gorilla一樣的壓縮算法,能夠將16個字節的數據點壓縮到平均1.37個字節。
Prometheus 2.0版本引入了全新的V3存儲引擎,提供了更高的寫入和查詢性能。本文主要分析該存儲引擎設計思路。

設計思路

Prometheus將Timeseries數據按2小時一個block進行存儲。每個block由一個目錄組成,該目錄裏包含:一個或者多個chunk文件(保存timeseries數據)、一個metadata文件、一個index文件(通過metric name和labels查找timeseries數據在chunk文件的位置)。最新寫入的數據保存在內存block中,達到2小時後寫入磁盤。為了防止程序崩潰導致數據丟失,實現了WAL(write-ahead-log)機制,將timeseries原始數據追加寫入log中進行持久化。刪除timeseries時,刪除條目會記錄在獨立的tombstone文件中,而不是立即從chunk文件刪除。
這些2小時的block會在後臺壓縮成更大的block,數據壓縮合並成更高level的block文件後刪除低level的block文件。這個和leveldb、rocksdb等LSM樹的思路一致。
這些設計和Gorilla的設計高度相似,所以Prometheus幾乎就是等於一個緩存TSDB。它本地存儲的特點決定了它不能用於long-term數據存儲,只能用於短期窗口的timeseries數據保存和查詢,並且不具有高可用性(宕機會導致歷史數據無法讀取)。
Prometheus本地存儲的局限性,所以它提供了API接口用於和long-term存儲集成,將數據保存到遠程TSDB上。該API接口使用自定義的protocol buffer over HTTP並且並不穩定,後續考慮切換為gRPC。

磁盤文件結構

內存中的block

內存中的block數據未刷盤時,block目錄下面主要保存wal文件。

./data/01BKGV7JBM69T2G1BGBGM6KB12
./data/01BKGV7JBM69T2G1BGBGM6KB12/meta.json
./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000002
./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000001

持久化的block

持久化的block目錄下wal文件被刪除,timeseries數據保存在chunk文件裏。index用於索引timeseries在wal文件裏的位置。

./data/01BKGV7JC0RY8A6MACW02A2PJD
./data/01BKGV7JC0RY8A6MACW02A2PJD/meta.json ./data/01BKGV7JC0RY8A6MACW02A2PJD/index ./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks ./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks/000001 ./data/01BKGV7JC0RY8A6MACW02A2PJD/tombstones

mmap

使用mmap讀取壓縮合並後的大文件(不占用太多句柄),建立進程虛擬地址和文件偏移的映射關系,只有在查詢讀取對應的位置時才將數據真正讀到物理內存。繞過文件系統page cache,減少了一次數據拷貝。查詢結束後,對應內存由Linux系統根據內存壓力情況自動進行回收,在回收之前可用於下一次查詢命中。因此使用mmap自動管理查詢所需的的內存緩存,具有管理簡單,處理高效的優勢。
從這裏也可以看出,它並不是完全基於內存的TSDB,和Gorilla的區別在於查詢歷史數據需要讀取磁盤文件。

Compaction

Compaction主要操作包括合並block、刪除過期數據、重構chunk數據。其中合並多個block成為更大的block,可以有效減少block個數,當查詢覆蓋的時間範圍較長時,避免需要合並很多block的查詢結果。
為提高刪除效率,刪除時序數據時,會記錄刪除的位置,只有block所有數據都需要刪除時,才將block整個目錄刪除。因此block合並的大小也需要進行限制,避免保留了過多已刪除空間(額外的空間占用)。比較好的方法是根據數據保留時長,按百分比(如10%)計算block的最大時長。

Inverted Index

Inverted Index(倒排索引)基於其內容的子集提供數據項的快速查找。簡而言之,我可以查看所有標簽為app=“nginx”的數據,而不必遍歷每一個timeseries,並檢查是否包含該標簽。
為此,每個時間序列key被分配一個唯一的ID,通過它可以在恒定的時間內檢索,在這種情況下,ID就是正向索引。
舉個栗子:如ID為9,10,29的series包含label app="nginx",則lable "nginx"的倒排索引為[9,10,29]用於快速查詢包含該label的series。

性能

在文章Writing a Time Series Database from Scratch裏,作者給出了benchmark測試結果為Macbook Pro上寫入達到2000萬每秒。這個數據比Gorilla論文中的目標7億次寫入每分鐘(1000千多萬每秒)提供了更高的單機性能。

參考

Writing a Time Series Database from Scratch
Prometheus官方介紹
時間序列數據的存儲和計算 - 開源時序數據庫解析(四)

Prometheus TSDB分析