1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        淺談Prometheus的數(shù)據(jù)存儲

        共 17155字,需瀏覽 35分鐘

         ·

        2021-08-14 06:01

        目錄

        • 1、概述

        • 2、時間序列

        • 3、二維模型

        • 4、存儲策略的演進(jìn)

          • 4.1 1.x 版本

          • 4.2 2.x 版本



        本文是結(jié)合耗子叔的視頻及 Prometheus 作者部分原文整理,加上部分個人理解而來,膜拜大神~

        1、概述

        Prometheus是一套開源的監(jiān)控&報警&時間序列數(shù)據(jù)庫的組合

        Prometheus內(nèi)部主要分為三大塊,Retrieval是負(fù)責(zé)定時去暴露的目標(biāo)頁面上去抓取采樣指標(biāo)數(shù)據(jù),Storage是負(fù)責(zé)將采樣數(shù)據(jù)寫磁盤,PromQLPrometheus提供的查詢語言模塊

        其有著非常高效的時間序列數(shù)據(jù)存儲方法,每個采樣數(shù)據(jù)僅僅占用3.5byte左右空間

        在早期有一個單獨的項目叫做 TSDB,但是,在2.1.x的某個版本,已經(jīng)不單獨維護(hù)這個項目了,直接將這個項目合并到了prometheus的主干上了

        prometheus每次抓取的數(shù)據(jù),對于操作者來說可見的格式(即在prometheus界面查詢到的值)

        requests_total{path="/status", method="GET", instance="10.0.0.1:80"} @1534317560938 94355

        意思就是在1534317560938這個時間點,10.0.0.1:80這個實例上,GET /status 這個請求的次數(shù)累計是 94355

        最終存儲在TSDB中的格式為

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

        2、時間序列

        • Data scheme數(shù)據(jù)標(biāo)識
        identifier -> (t0, v0), (t1, v1), (t2, v2), (t3, v3), ...
        • Prometheus Data Model數(shù)據(jù)模型
        <metric name>{<label name>=<label value>, ...}
        • Typical set of series identifiers

        • Query 查詢

        __name__="requests_total":查詢所有屬于requests_total的序列

        method="PUT|POST":查詢所有序列中方法是PUTPOST的序列

        3、二維模型

        • Write 寫:每個目標(biāo)暴露成百上千個不同的時間序列,寫入模式是完全垂直和高度并發(fā)的,因為來自每個目標(biāo)的樣本是獨立的

        • Query 查:查詢數(shù)據(jù)時可以并行和批處理

        series
          ^
          │   . . . . . . . . . . . . . . . . .   . . . . .   {__name__="request_total", method="GET"}
          │     . . . . . . . . . . . . . . . . . . . . . .   {__name__="request_total", method="POST"}
          │         . . . . . . .
          │       . . .     . . . . . . . . . . . . . . . .                  ...
          │     . . . . . . . . . . . . . . . . .   . . . .
          │     . . . . . . . . . .   . . . . . . . . . . .   {__name__="errors_total", method="POST"}
          │           . . .   . . . . . . . . .   . . . . .   {__name__="errors_total", method="GET"}
          │         . . . . . . . . .       . . . . .
          │       . . .     . . . . . . . . . . . . . . . .                  ...
          │     . . . . . . . . . . . . . . . .   . . . .
          v
            <-------------------- time --------------------->

        二維模型中橫軸表示時間,縱軸表示各數(shù)據(jù)點

        這類設(shè)計會帶來的問題如下

        存儲問題

        如上圖所示,在二維模型中的讀寫差別是很大的

        (時間序列查詢)讀時帶來的隨機(jī)讀問題和查詢帶來的隨機(jī)寫問題,(查詢)讀往往會比寫更復(fù)雜,這是很慢的。盡管用了SSD,但會帶來寫放大的問題,SSD4k寫,256k刪除,SSD之所以快,實際上靠的是算法,因此在文件碎片如此大的情況下,都是不能滿足的

        理想狀態(tài)下的寫應(yīng)該是順序?qū)?、批量寫,對于相同的時間序列讀應(yīng)該也是順序讀

        4、存儲策略的演進(jìn)

        4.1 1.x 版本

        1.x 版本下,存儲情況是這樣的

        • 每個時間序列都對應(yīng)一個文件
        • 在內(nèi)存中批量處理 1kb 的的 chunk
           ┌──────────┬─────────┬─────────┬─────────┬─────────┐           series A
           └──────────┴─────────┴─────────┴─────────┴─────────┘
                  ┌──────────┬─────────┬─────────┬─────────┬─────────┐    series B
                  └──────────┴─────────┴─────────┴─────────┴─────────┘
                                      . . .
         ┌──────────┬─────────┬─────────┬─────────┬─────────┬─────────┐   series XYZ
         └──────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
           chunk 1    chunk 2   chunk 3     ...

        存在的問題:

        • chunk保存在內(nèi)存中,如果應(yīng)用程序或節(jié)點崩潰,它可能會丟失

        • 由于時間序列的維度很多,對于的文件個數(shù)也會很多,這可能耗盡操作系統(tǒng)的inode

        • 上千的chunk保存在硬盤需要持久化,可能會導(dǎo)致磁盤I/O非常繁忙

        • 磁盤I/O打開很多的文件,會導(dǎo)致非常高的延遲

        • 舊數(shù)據(jù)需要清理,這可能會導(dǎo)致SSD的寫放大

        • 非常大的CPU、內(nèi)存、磁盤資源消耗

        • 序列的丟失和變動

        例如一些時間序列變得不活躍,而另一些時間序列變得活躍,原因在于例如k8s中應(yīng)用程序的連續(xù)自動擴(kuò)展和頻繁滾動更新帶來的實例的ip等變化,每天可能會創(chuàng)建數(shù)萬個新應(yīng)用程序?qū)嵗?,以及全新的時間序列集

        因此,即使整個基礎(chǔ)設(shè)施的規(guī)模大致保持不變,隨著時間的推移,數(shù)據(jù)庫中的時間序列也會線性增長。即使Prometheus服務(wù)器能夠收集1000萬個時間序列的數(shù)據(jù),但如果必須在10億個序列中找到數(shù)據(jù),查詢性能會受到很大影響

        series
          ^
          │   . . . . . .
          │   . . . . . .
          │   . . . . . .
          │               . . . . . . .
          │               . . . . . . .
          │               . . . . . . .
          │                             . . . . . .
          │                             . . . . . .
          │                                         . . . . .
          │                                         . . . . .
          │                                         . . . . .
          v
            <-------------------- time --------------------->

        4.2 2.x 版本

        2.x 時代的存儲布局

        https://github.com/prometheus/prometheus/blob/release-2.25/tsdb/docs/format/README.md

        4.2.1 數(shù)據(jù)存儲分塊

        • 01xxxxx 數(shù)據(jù)塊

          ULID,和UUID一樣,但是是按照字典和編碼的創(chuàng)建時間排序的

        • chunk 目錄

          包含各種系列的原始數(shù)據(jù)點塊,但不再是每個序列對應(yīng)一個單一的文件

        • index 數(shù)據(jù)索引

          可以通過標(biāo)簽找到數(shù)據(jù),這里保存了LabelSeries的數(shù)據(jù)

        • meta.json 可讀元數(shù)據(jù)

          對應(yīng)存儲和它包含的數(shù)據(jù)的狀態(tài)

        • tombstone

          刪除的數(shù)據(jù)將被記錄到這個文件中,而不是從塊文件中刪除

        • wal 預(yù)寫日志 Write-Ahead Log

          WAL段將被截斷到checkpoint.X目錄中

        • chunks_head

          在內(nèi)存中的數(shù)據(jù)

        • 數(shù)據(jù)將每 2 小時保存到磁盤中

        • WAL 用于數(shù)據(jù)恢復(fù)

        • 2 小時塊可以高效查詢范圍數(shù)據(jù)

        分塊存儲后,每個目錄都是獨立的存儲目錄,結(jié)構(gòu)如下:

        $ tree ./data
        ./data
        ├── b-000001
        │   ├── chunks
        │   │   ├── 000001
        │   │   ├── 000002
        │   │   └── 000003
        │   ├── index
        │   └── meta.json
        ├── b-000004
        │   ├── chunks
        │   │   └── 000001
        │   ├── index
        │   └── meta.json
        ├── b-000005
        │   ├── chunks
        │   │   └── 000001
        │   ├── index
        │   └── meta.json
        └── b-000006
            ├── meta.json
            └── wal
                ├── 000001
                ├── 000002
                └── 000003

        分塊存儲對應(yīng)著Blocks,可以看做是小型數(shù)據(jù)庫

        • 將數(shù)據(jù)分成互不重疊的塊

          每個塊都充當(dāng)一個完全獨立的數(shù)據(jù)庫

          包含其時間窗口的所有時間序列數(shù)據(jù)

          有自己的索引和塊文件集

        • 每個數(shù)據(jù)塊都是不可變的

        • 當(dāng)前塊可以追加數(shù)據(jù)

        • 所有新數(shù)據(jù)都寫入內(nèi)存數(shù)據(jù)庫

        • 為了防止數(shù)據(jù)丟失,還寫了一個臨時 WAL

        t0            t1             t2             t3             now
         ┌───────────┐  ┌───────────┐  ┌───────────┐  ┌───────────┐
         │           │  │           │  │           │  │           │                 ┌────────────┐
         │           │  │           │  │           │  │  mutable  │ <─── write ──── ┤ Prometheus │
         │           │  │           │  │           │  │           │                 └────────────┘
         └───────────┘  └───────────┘  └───────────┘  └───────────┘                        ^
               └──────────────┴───────┬──────┴──────────────┘                              │
                                      │                                                  query
                                      │                                                    │
                                    merge ─────────────────────────────────────────────────┘

        4.2.2 block 合并

        上面分離了block后,會帶來的問題

        • 當(dāng)查詢多個塊時,必須將它們的結(jié)果合并到一個整體結(jié)果中
        • 如果我們需要一個星期的查詢,它必須合并 80 多個 block 塊
        t0             t1            t2             t3             t4             now
         ┌────────────┐  ┌──────────┐  ┌───────────┐  ┌───────────┐  ┌───────────┐
         │ 1          │  │ 2        │  │ 3         │  │ 4         │  │ 5 mutable │    before
         └────────────┘  └──────────┘  └───────────┘  └───────────┘  └───────────┘
         ┌─────────────────────────────────────────┐  ┌───────────┐  ┌───────────┐
         │ 1              compacted                │  │ 4         │  │ 5 mutable │    after (option A)
         └─────────────────────────────────────────┘  └───────────┘  └───────────┘
         ┌──────────────────────────┐  ┌──────────────────────────┐  ┌───────────┐
         │ 1       compacted        │  │ 3      compacted         │  │ 5 mutable │    after (option B)
         └──────────────────────────┘  └──────────────────────────┘  └───────────┘

        4.2.3 數(shù)據(jù)保留

                              |
         ┌────────────┐  ┌────┼─────┐  ┌───────────┐  ┌───────────┐  ┌───────────┐
         │ 1          │  │ 2  |     │  │ 3         │  │ 4         │  │ 5         │   . . .
         └────────────┘  └────┼─────┘  └───────────┘  └───────────┘  └───────────┘
                              |
                              |
                     retention boundary

        1塊可以被安全刪除,第2塊必須保持直到它完全超出邊界

        塊合并帶來的影響

        • 塊壓縮可能使塊太大而無法刪除
        • 需要限制塊的大小
        最大塊大小 = 保留窗口 * 10%

        4.2.4 查詢和索引

        主要特點

        • 使用倒排索引,倒排索引提供基于其內(nèi)容子集的數(shù)據(jù)項的快速查找。例如,可以查找所有具有標(biāo)簽的系列,app=”nginx"而無需遍歷每個系列并檢查它是否包含該標(biāo)簽

        • 正向索引,為每個序列分配一個唯一的ID,通過它可以在恒定的時間內(nèi)檢索

        一個目錄中保存了很多Series,如果想要根據(jù)一個Label來查詢對應(yīng)的所有Series,具體流程是什么呢

        為每個Series中的所有Label都建立了一個倒排索引

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

        正向索引的引入,給每個Series分配了一個ID,便于組合查詢

        LabelSeriesID
        __name__="requests_total"1001
        path="/status"1001
        method="GET"1001
        instance=”10.0.0.1:80”1001

        例如,如果查詢的語句是:__name __ =“requests_total” AND app =“nginx”

        需要先分別找出對應(yīng)的倒排索引,再求交集,由此會帶來一定的時間復(fù)雜度O(N2,為了減少時間復(fù)雜度,實際上倒排索引中的SeriesID是有序的,那么采取ZigZag的查找方式,可以保證在O(N)的時間復(fù)雜來找到最終的結(jié)果

        4.2.6 WAL

        通過mmap(不經(jīng)過文件系統(tǒng)的寫數(shù)據(jù)方式),同時在內(nèi)存和WAL預(yù)寫日志Write-Ahead Log中保存數(shù)據(jù),即可以保證數(shù)據(jù)的持久不丟失,又可以保證崩潰之后從故障中恢復(fù)的時間很短,因為是從內(nèi)存中恢復(fù)

        4.2.7 小結(jié)

        新的存儲結(jié)構(gòu)帶來的好處

        • 在查詢某個時間范圍時,可以輕松忽略該范圍之外的所有數(shù)據(jù)塊。它通過減少檢查數(shù)據(jù)集來輕松解決數(shù)據(jù)流失問題
        • 當(dāng)完成一個塊時,可以通過順序?qū)懭胍恍┹^大的文件來保存內(nèi)存數(shù)據(jù)庫中的數(shù)據(jù)。避免任何寫放大,并同樣為 SSDHDD提供服務(wù)
        • 保留了V2的良好特性,即最近查詢最多的塊總是在內(nèi)存中的
        • 不再受限于固定的1KiB塊大小來更好地對齊磁盤上的數(shù)據(jù)??梢赃x擇對單個數(shù)據(jù)點和所選壓縮格式最有意義的任何大小
        • 刪除舊數(shù)據(jù)變得非常便宜和即時,只需要刪除一個目錄。在舊版本的存儲中,必須分析和重寫多達(dá)數(shù)億個文件,這可能需要數(shù)小時才能收斂

        參考資料

        [1]

        https://www.bilibili.com/video/BV1a64y1X7ys

        [2]

        https://fabxc.org/tsdb/

        [3]

        http://ganeshvernekar.com/blog/prometheus-tsdb-the-head-block/

        瀏覽 67
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            西西4444大胆无码视频| 久久嫩草| 你懂的在线观看视频| 中文字幕浅井香舞被黑人俘虏 | h视频| 国产AV无码成人精品毛片| 日韩不卡高清在线观看视频| 欧美特大黄| 麻豆传媒电影| 欲色av|