導讀緩存
Apache Pulsar是一個多租戶、高性能的服務間消息傳輸解決方案,支持多租戶、低延時、讀寫分離、跨地域複製、快速擴容、靈活容錯等特性。騰訊數據平臺部MQ團隊對Pulsar作了深刻調研以及大量的性能和穩定性方面優化,目前已經在TDbank落地上線。本文是Pulsar技術系列中的一篇,主要簡單梳理了Pulsar消息存儲與BookKeeper存儲文件的清理機制。其中,BookKeeper能夠理解爲一個NoSQL的存儲系統,默認使用RockDB存儲索引數據。服務器
做者介紹
微信
鮑明宇數據結構
騰訊TEG數據平臺部高級工程師
Apache Pulsar Contributorapp
熱衷於開源技術,在消息隊列領域有豐富經驗,目前致力於Pulsar的落地和推廣運維
Pulsar消息存儲編輯器
Pulsar的消息存儲在BookKeeper中,BookKeeper是一個胖客戶的系統,客戶端部分稱爲BookKeeper,服務器端集羣中的每一個存儲節點稱爲bookie。Pulsar系統的broker做爲BookKeeper存儲系統的客戶端,經過BookKeeper提供的客戶端SDK將Pulsar的消息存儲到bookies集羣中。svg
Pulsar中的每一個topic的每一個分區(非分區topic,能夠按照分區0理解,分區topic的編號是從0開始的),會對應一系列的ledger,而每一個ledger只會存儲對應分區下的消息。對於每一個分區同時只會有一個ledger處於open便可寫狀態。微服務
Pulsar在生產消息,存儲消息時,會先找到當前分區使用的ledger ,而後生成當前消息對應的entry ID,entry ID在同一個ledger內是遞增的。非批量生產的狀況(producer 端能夠配置這個參數,默認是批量的),一個entry 中包含一條消息。批量方式下,一個entry可能包含多條消息。而bookie中只會按照entry維度進行寫入、查找、獲取。性能
所以,每一個Pulsar下的消息的msgID 須要有四部分組成(老版本由三部分組成),分別爲(ledgerID,entryID,partition-index,batch-index),其中,partition-index 在非分區topic的時候爲-1,batch-index在非批量消息的時候爲-1。
每一個ledger,當存在的時長或保存的entry個數超過閾值後會進行切換,同一個partition下的,新的消息會存儲到下一個ledger中。Ledger只是一個邏輯概念,是數據的一種邏輯組裝維度,並無對應的實體。
BookKeeper集羣中的每一個bookie 節點收到消息後,數據會分三部分進行存儲處理,分別爲:journal 文件、entryLog 文件、索引文件。
其中journal文件,entry數據是按照wal方式寫入的到journal文件中,每一個journal文件有大小限制,當超過單個文件大小限制的時候會切換到下一個文件繼續寫,由於journal文件是實時刷盤的,因此爲了提升性能,避免相互之間的讀寫IO相互影響,建議存儲目錄與存儲entrylog的目錄區分開,而且給每一個journal文件的存儲目錄單獨掛載一塊硬盤(建議使用ssd硬盤)。journal文件只會保存保存幾個,超過配置個數的文件將會被刪除。entry 存儲到journal文件徹底是隨機的,先到先寫入,journal文件是爲了保證消息不丟失而設計的。
以下圖所示,每一個bookie收到增長entry的請求後,會根據ledger id映射到存儲到那個journal目錄和entry log目錄,entry數據會存儲在對應的目錄下。目前bookie不支持在運行過程當中變動存儲目錄(使用過程當中,增長或減小目錄會致使部分的數據查找不到)。
以下圖所示,bookie收到entry寫入請求後,寫入journal文件的同時,也會保存到write cache中,write cache分爲兩部分,一部分是正在寫入的write cache, 一部分是正在正在刷盤的部分,兩部分交替使用。
write cache中有索引數據結構,能夠經過索引查找到對應的entry,write cache中的索引是內存級別的,基於bookie本身定義的ConcurrentLongLongPairHashMap結構實現。
另外,每一個entorylog的存儲目錄,會對應一個SingleDirectoryDbLedgerStorage類實例對象,而每一個SingleDirectoryDbLedgerStorage對象裏面會有一個基於RockDB實現的索引結構,經過這個索引能夠快速的查到每一個entry存儲在哪一個entrylog文件中。每一個write cache在增長entry的時候會進行排序處理,在同一個write cache,同一個ledger下的數據是相鄰有序的,這樣在write cache中的數據flush到entrylog文件時,使得寫入到entrylog文件中的數據是局部有序的,這樣的設計可以極大的提升後續的讀取效率。
SingleDirectoryDbLedgerStorage中的索引數據也會隨着entry的刷盤而刷盤到索引文件中。在bookie宕機重啓時,能夠經過journal文件和entry log文件還原數據,保證數據不丟失。
Pulsar consumer 在消費數據的時候,作了多層的緩存加速處理,以下圖所示:
獲取數據的順序以下:
-
在broker端的entry cache中獲取,若是沒有在繼續; -
在bookie的write cache正在寫的這部分中獲取,若是沒有則繼續; -
在bookie的write cache正在刷盤的這部分中獲取,若是沒有則繼續; -
從bookie的read cache中獲取,若是沒有則繼續; -
經過索引讀取磁盤上的entry log文件。
上面每一步,若是能獲取到數據,都會直接返回,跳事後面的步驟。若是是從磁盤文件中獲取的數據,會在返回的時候將數據存儲到read cache中,另外若是是讀取磁盤的操做,會多讀取一部分磁盤上的時候,由於存儲的時候有局部有序的處理,獲取相鄰數據的機率很是大,這種處理的話會極大的提升後續獲取數據的效率。
咱們在使用的過程當中,應儘可能避免或減小出現消費過老數據即觸發讀取磁盤文件中的消息的場景,以避免對總體系統的性能形成影響。
BookKeeper的GC機制
BookKeeper中的每一個bookie都會週期的進行數據清理操做,默認15分鐘檢查處理一次,清理的主要流程以下
-
清理bookie存儲的ledger id(bookie內存儲的ledger id與zk上面存儲的 ledger id作比較,若是zk上面沒有則刪除bookie中存儲的ledger id); -
統計每一個entry log中存活的entry佔比,當前entry log 存活的ledger個數爲0時刪除這個entry log; -
根據entry log的元數據信息,清理entry log 文件(當entry log包含的全部ledger id所有失效時刪除); 壓縮entry log文件 ,分別在當前entry log文件下存活的entry比例在0.5-默認週期1天(major gc) 或比例0.2-默認週期1個小時(minor gc) 的時候,Compaction entry log文件,將老的文件中存活的entry轉移新的文件中,而後將老的entry log文件刪除,單次的GC若是處理的entry log文件比較大的時候可能耗時比較長。
經過上面的流程,咱們能夠了解bookie在清理entrylog文件時的大致流程。
須要特別說明的是,ledger是不是能夠刪除的,徹底是客戶端的觸發的,在Pulsar中是broker觸發的。
broker端有周期的處理線程(默認2分鐘),清理已經消費過的消息所在的ledger機制,獲取topic中包含的cursor最後確認的消息,將這個topic包含的ledger列表中,在這個id以前的(注意不包含當前的ledger id)所有刪除(包括zk中的元數據,同時通知bookie刪除對應的ledger)。
運營中遇到的問題分析
在運用的過程當中咱們屢次遇到了bookie磁盤空間不足的場景,bookie中存儲了大量的entry log文件。比較典型的緣由主要有以下兩個。
緣由一:
生產消息過於分散,例如,舉個極端的場景,1w個topic,每一個topic生產一條,1w個topic順序生產。這樣每一個topic 對應的ledger短期內不會由於時長或者存儲大小進行切換,active狀態的ledger id分散在大量的entry log文件中。這些entry log文件是不能刪除或者及時壓縮的。
若是遇到這種場景,能夠經過重啓,強制ledger進行切換進行處理。固然若是這個時候消費進行沒有跟上,消費的last ack位置所在的ledger也是處於active狀態的,不能進行刪除。
緣由二:
GC時間過程,若是現存的enrylog文件比較多,且大量符合minor或major gc閾值,這樣,單次的minor gc或者major gc時間過長,在這段時間內是不能清理過時的entry log文件。
這是因爲單次清理流程的順序執行致使的,只有上次一輪執行完,纔會執行下一次。目前,這塊也在提優化流程,避免子流程執行實現過長,對總體產生影響。
小結
本文首先,介紹了Pulsar消息的存儲組織形式,存儲流程和消息的獲取過程。其次,對單個bookie的GC流程作了詳盡的說明。在Pulsar的使用過程當中,應該儘可能避免消費過舊的歷史數據即須要讀取磁盤獲取數據的場景。
在運維bookie的過程當中,是不能在運行過程當中調整存儲目錄的個數的,在部署時須要對容量進行充分的評估。若是須要在運營的過程當中進行調整時,須要對單個的bookie節點進行擴縮容處理。
往期
推薦
《200 行代碼告訴你 TDMQ 中 Pulsar 廣播如何實現》
掃描下方二維碼關注本公衆號,
瞭解更多微服務、消息隊列的相關信息!
解鎖超多鵝廠周邊!
本文分享自微信公衆號 - 騰訊雲中間件(gh_6ea1bc2dd5fd)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。