Prometheus 2 中的 TSDB 分析

Prometheus 2 中的 TSDB 分析

Prometheus 2 中的時間序列資料庫(TSDB)是一個很好的工程解決方案的例子,它在資料累積和查詢效能速度以及資源效率方面比 Prometheus 2 中的 v1 儲存有了顯著的改進。我們正在 Percona 監控和管理 (PMM) 中實作 Prometheus 2,我有機會了解 Prometheus 2 TSDB 的效能。在本文中我將討論這些觀察的結果。

Prometheus 平均工作負載

對於習慣處理主要用途資料庫的人來說,典型的 Prometheus 工作負載非常有趣。資料累積的速度趨於穩定:通常,您監控的服務發送大致相同數量的指標,並且基礎設施變化相對較慢。
資訊請求可能來自不同的來源。其中一些,例如警報,也往往是穩定和可預測的。其他因素(例如用戶請求)可能會導致峰值,儘管這對於大多數工作負載來說並不常見。

負載測試

在測試期間,我專注於累積數據的能力。我使用以下腳本將使用 Go 2.3.2(作為 PMM 1.10.1 的一部分)編譯的 Prometheus 1.14 部署到 Linode 服務: StackScript。為了盡可能真實地產生負載,請使用此 StackScript 我啟動了多個 MySQL 節點,並施加了實際負載(Sysbench TPC-C 測試),每個節點模擬了 10 個節點。 Linux/MySQL。
以下所有測試均在具有八個虛擬核心和 32 GB 記憶體的 Linode 伺服器上執行,運行 20 個負載模擬來監控兩百個 MySQL 實例。或者,用 Prometheus 的術語來說,800 個目標、每秒 440 次抓取、每秒 380 萬個樣本和 1,7 萬個活躍時間序列。

設計

傳統資料庫(包括 Prometheus 1.x 使用的資料庫)的典型方法是 記憶體限制。如果它不足以處理負載,您將遇到高延遲,並且某些請求將無法滿足。 Prometheus 2 中的記憶體使用情況透過金鑰配置 storage.tsdb.min-block-duration,它決定了記錄在刷新到磁碟之前在記憶體中保存多長時間(預設為 2 小時)。所需記憶體量取決於時間序列、標籤和抓取的數量以及淨傳入資料流。在磁碟空間方面,Prometheus 的目標是每筆記錄(樣本)使用 3 個位元組。另一方面,對記憶體的要求要高得多。

雖然可以配置區塊大小,但不建議手動配置,因此您必須為 Prometheus 提供其為您的工作負載請求的盡可能多的記憶體。
如果沒有足夠的記憶體來支援傳入的指標流,Prometheus 將因記憶體不足而崩潰或被 OOM killer 捕獲。
新增交換來延遲 Prometheus 記憶體耗盡時的崩​​潰並沒有太大幫助,因為使用它會導致記憶體消耗激增。我認為是 Go、它的垃圾收集器以及它處理交換的方式。
另一種有趣的方法是將頭塊設定在特定時間刷新到磁碟,而不是從進程啟動時開始計算。

Prometheus 2 中的 TSDB 分析

從圖中可以看出,磁碟刷新每兩小時發生一次。如果將 min-block-duration 參數變更為一小時,則這些重設將在半小時後開始每小時發生一次。
如果你想在你的 Prometheus 安裝中使用這個和其他圖表,你可以使用這個 儀表板。它是為 PMM 設計的,但經過微小修改後它就適合任何 Prometheus 安裝。
我們有一個活動區塊,稱為頭塊,它儲存在記憶體中;包含舊資料的區塊可以透過 mmap()。這樣就無需單獨配置緩存,但也意味著如果您想查詢早於頭塊可以容納的數據,則需要為作業系統緩存留出足夠的空間。
這也意味著Prometheus的虛擬記憶體消耗會顯得相當高,這是沒什麼好擔心的。

Prometheus 2 中的 TSDB 分析

另一個有趣的設計點是使用 WAL(預寫日誌)。從儲存文件中可以看出,Prometheus 使用 WAL 來避免崩潰期間的損失。不幸的是,保證資料持久性的具體機制沒有得到很好的記錄。 Prometheus 2.3.2 版本每 10 秒將 WAL 刷新到磁碟,且此參數不可由使用者配置。

壓縮

Prometheus TSDB 的設計類似於日誌結構化合併 (LSM) 儲存:頭部區塊定期刷新到磁碟,而壓縮機制將多個區塊合併在一起,以避免在查詢期間掃描過多的區塊。在這裡您可以看到我在 24 小時的負載後在測試系統上觀察到的區塊數。

Prometheus 2 中的 TSDB 分析

如果您想了解有關儲存庫的更多信息,您可以查看 meta.json 文件,其中包含有關可用區塊及其形成方式的資訊。

{
       "ulid": "01CPZDPD1D9R019JS87TPV5MPE",
       "minTime": 1536472800000,
       "maxTime": 1536494400000,
       "stats": {
               "numSamples": 8292128378,
               "numSeries": 1673622,
               "numChunks": 69528220
       },
       "compaction": {
               "level": 2,
               "sources": [
                       "01CPYRY9MS465Y5ETM3SXFBV7X",
                       "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                       "01CPZ6NR4Q3PDP3E57HEH760XS"
               ],
               "parents": [
                       {
                               "ulid": "01CPYRY9MS465Y5ETM3SXFBV7X",
                               "minTime": 1536472800000,
                               "maxTime": 1536480000000
                       },
                       {
                               "ulid": "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                               "minTime": 1536480000000,
                               "maxTime": 1536487200000
                       },
                       {
                               "ulid": "01CPZ6NR4Q3PDP3E57HEH760XS",
                               "minTime": 1536487200000,
                               "maxTime": 1536494400000
                       }
               ]
       },
       "version": 1
}

Prometheus 中的壓縮與頭部區塊刷新到磁碟的時間有關。此時可以執行幾個這樣的操作。

Prometheus 2 中的 TSDB 分析

看起來壓縮不受任何限制,並且可能在執行期間導致磁碟 I/O 出現大幅峰值。

Prometheus 2 中的 TSDB 分析

CPU負載峰值

Prometheus 2 中的 TSDB 分析

當然,這對系統效能有相當負面的影響,對LSM儲存也是一個嚴峻的挑戰:如何緊湊地支援高查詢率,又不造成太大的開銷?
壓縮期間記憶體的使用看起來也很有趣。

Prometheus 2 中的 TSDB 分析

我們可以看到,壓縮之後,大部分記憶體的狀態從“已快取”變為“可用”,這意味著潛在的有價值的資訊已從那裡被刪除。我不知道它是否在這裡使用 fadvice() 或者其他一些最小化技術,或者是由於在壓縮過程中緩存被從被破壞的區塊中釋放出來而導致的?

故障後恢復

從失敗中恢復需要時間,這是有充分理由的。對於每秒一百萬筆記錄的傳入流,考慮到 SSD 驅動器,我必須等待大約 25 分鐘才能進行恢復。

level=info ts=2018-09-13T13:38:14.09650965Z caller=main.go:222 msg="Starting Prometheus" version="(version=2.3.2, branch=v2.3.2, revision=71af5e29e815795e9dd14742ee7725682fa14b7b)"
level=info ts=2018-09-13T13:38:14.096599879Z caller=main.go:223 build_context="(go=go1.10.1, user=Jenkins, date=20180725-08:58:13OURCE)"
level=info ts=2018-09-13T13:38:14.096624109Z caller=main.go:224 host_details="(Linux 4.15.0-32-generic #35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 1bee9e9b78cf (none))"
level=info ts=2018-09-13T13:38:14.096641396Z caller=main.go:225 fd_limits="(soft=1048576, hard=1048576)"
level=info ts=2018-09-13T13:38:14.097715256Z caller=web.go:415 component=web msg="Start listening for connections" address=:9090
level=info ts=2018-09-13T13:38:14.097400393Z caller=main.go:533 msg="Starting TSDB ..."
level=info ts=2018-09-13T13:38:14.098718401Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536530400000 maxt=1536537600000 ulid=01CQ0FW3ME8Q5W2AN5F9CB7R0R
level=info ts=2018-09-13T13:38:14.100315658Z caller=web.go:467 component=web msg="router prefix" prefix=/prometheus
level=info ts=2018-09-13T13:38:14.101793727Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536732000000 maxt=1536753600000 ulid=01CQ78486TNX5QZTBF049PQHSM
level=info ts=2018-09-13T13:38:14.102267346Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536537600000 maxt=1536732000000 ulid=01CQ78DE7HSQK0C0F5AZ46YGF0
level=info ts=2018-09-13T13:38:14.102660295Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536775200000 maxt=1536782400000 ulid=01CQ7SAT4RM21Y0PT5GNSS146Q
level=info ts=2018-09-13T13:38:14.103075885Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536753600000 maxt=1536775200000 ulid=01CQ7SV8WJ3C2W5S3RTAHC2GHB
level=error ts=2018-09-13T14:05:18.208469169Z caller=wal.go:275 component=tsdb msg="WAL corruption detected; truncating" err="unexpected CRC32 checksum d0465484, want 0" file=/opt/prometheus/data/.prom2-data/wal/007357 pos=15504363
level=info ts=2018-09-13T14:05:19.471459777Z caller=main.go:543 msg="TSDB started"
level=info ts=2018-09-13T14:05:19.471604598Z caller=main.go:603 msg="Loading configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499156711Z caller=main.go:629 msg="Completed loading of configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499228186Z caller=main.go:502 msg="Server is ready to receive web requests."

恢復過程的主要問題是其記憶體消耗高。雖然伺服器在同樣的記憶體情況下可能還能穩定運行,但是如果因為OOM而崩潰的話,有可能無法恢復。我發現的唯一解決方案是停用資料收集,啟動伺服器,讓其恢復並在啟用收集的情況下重新啟動。

熱身

預熱期間要注意的另一個行為是啟動後低效能與高資源消耗的比率。在某些(但不是全部)運行過程中,我觀察到明顯的 CPU 和記憶體負載。

Prometheus 2 中的 TSDB 分析

Prometheus 2 中的 TSDB 分析

記憶體使用率下降表示 Prometheus 無法從一開始就配置所有集合,並且一些資訊遺失。
我還沒有弄清楚CPU和記憶體負載過高的具體原因。我懷疑這是由於在頭部區塊中高頻率地創建了新的時間序列。

CPU負載峰值

除了壓縮會產生相當高的 I/O 負載之外,我還注意到每兩分鐘 CPU 負載就會出現明顯的峰值。當傳入流量較高時,峰值會更長,這似乎是由 Go 的垃圾收集器引起的,至少有些核心已滿載。

Prometheus 2 中的 TSDB 分析

Prometheus 2 中的 TSDB 分析

這些跳躍並不是那麼微不足道。看起來,當它們發生時,內部的 Prometheus 入口點和指標變得不可用,從而導致同一時間段內出現資料缺口。

Prometheus 2 中的 TSDB 分析

您還會注意到 Prometheus 匯出器掛起了一秒鐘。

Prometheus 2 中的 TSDB 分析

我們可以看到與垃圾收集(GC)的相關性。

Prometheus 2 中的 TSDB 分析

結論

Prometheus 2 中的 TSDB 速度很快,能夠使用相當適中的硬體處理數百萬個時間序列,同時每秒進行數千次寫入。 CPU 和磁碟 I/O 利用率也令人印象深刻。我的範例顯示每個核心每秒最多可處理 200 個指標。

在規劃擴展時,需要記住要有足夠的內存,而且必須是真正的內存。我觀察到的記憶體使用量是每秒 5 次寫入傳入流量大約需要 100 GB,與作業系統快取結合使用時,總共需要使用大約 000 GB 的記憶體。

當然,要控制 CPU 和磁碟 I/O 峰值還有很多工作要做,考慮到 Prometheus 2 TSDB 與 InnoDB、TokuDB、RocksDB、WiredTiger 相比還很年輕,這並不奇怪,但它們在生命週期的早期都遇到過類似的問題。

來源: www.habr.com

為具有 DDoS 保護、VPS VDS 服務器的站點購買可靠的主機 🔥 購買具備 DDoS 防護的可靠網站寄存服務,包括 VPS 和 VDS 伺服器 | ProHoster