引言
對于一款訊息中間件來說,優良的資料存盤設計,是實作高性能訊息吞吐以及訊息查詢的關鍵所在,因為訊息中間件對于外部來說就是發訊息消費訊息的一個平臺基礎設施,但是從其本身來說,需要將海量訊息資料資訊持久化在RocketMQ節點所在的服務器上,這樣即便是服務器斷電,重啟等情況下,也不至于丟失訊息資料,另外在進行訊息消費的時候,RocketMQ如何能借助自身的存盤設計快速檢索到對應的訊息也是非常重要的,因此本文主要對RocketMQ存盤設計進行了設計分析,
存盤結構
RocketMQ對應的存盤檔案主要包括三類,分別是Commitlog檔案、ConsumeQueue檔案以及Index檔案,每一個檔案都有其特殊的使命,

Commitlog檔案
當生產者將訊息發送到RocketMQ的Broker之后,需要將訊息進行持久化存盤,防止訊息資料丟失,RocketMQ將訊息資料寫入存盤檔案CommitLog中,按照訊息的發送順序寫入檔案當中,每個檔案的大小約為1G,當達到檔案大小限制后,就會創建新的CommitLog檔案,RocketMQ作為訊息中間件來說,最主要的資料流程就是基于主題的發布-訂閱模式進行訊息的發布以及消費,那么當消費者根據自己訂閱的Topic進行訊息消費的時候,Broker怎么在那么多的CommitLog檔案中找到對應Topic的訊息資料呢?

大家可以想一想,CommitLog檔案中的訊息資料是一條一條順序寫的,最笨的方法就是遍歷檔案,作為一款高性能的訊息中間件,顯然這不是一個好的解決方案,就像從資料庫查詢資料的時候,遍歷的效率肯定是很低的,那么我們可不可以借助資料庫提升資料查詢的方式,使用索引來加快訊息資料的查詢呢?答案是肯定的,就像Mysql中的索引本身需要檔案保存一樣,在RocketMQ中頁有單獨保存索引的檔案,就是ConsumerQueue檔案,
ConsumerQueue檔案
在RocketMQ中,每個Topic對應多個MessageQueue,每個MessageQueue對應一組ConsumerQueue檔案索引檔案,ConsumerQueue檔案中存盤了訊息相對于CommitLog檔案的offset偏移量,CommitLog檔案本身實際上也是通過偏移量來進行命名如第一個檔案是0000000000000,那么第二檔案就是訊息總量之和00000001232321,往后新的檔案再進行累計,為什么這么做呢?主要就是在進行訊息查找的時候根據訊息的偏移量通過二分查找快速定位具體的CommitLog檔案,提升訊息查找效率,需要說明的是,Broker在進行訊息寫入CommitLog檔案中就會異步將其對應的偏移量寫入ConsumerQueue檔案中,

在ConsumerQueue檔案中實際存盤了CommitLog檔案的offset偏移量、訊息長度以及tag的hashcode,組成20位元組的block塊,其在Broker上面的存盤路徑大致是:…/store/consumequeue/{topic}/{queueid}/{file},其中topic就是生產中訂閱的主題,因此消費者在消費訊息的時候,Broker會根據其對應的Topic找到對應的 ConsumerQueue檔案,進而找到其索引位置,再到CommitLog檔案中直接定位具體的訊息,
Index檔案
另外RocketMQ的特性功能就是可以實作按照訊息的屬性進行訊息搜索,即建立了索引 Key 的 hashcode 與物理偏移量的映射關系,根據 key 先快速定義到 commitlog 檔案,

存盤性能設計精髓
上文中為大家闡述了RocketMQ關于存盤結構的設計,優秀的存盤設計師實作高性能讀寫的前提,那么除了存盤結構的設計,RocketMQ也使用了一些性能優化手段來實作其強大的訊息吞吐能力,
訊息順序寫
前文中說過,訊息進入RocketMQ之后,訊息資料是通過順序寫的方式落到CommitLog檔案中的,那么這里面就涉及兩個問題,為什么進行順序寫以及是不是直接寫磁盤檔案,
1、為什么要順序寫?
當新的資料到來時,只要在之前的檔案末尾進行資料追加就可以,這樣的資料寫入效率要比隨機寫入的效率高,
2、每次資料寫入的時候是直接寫磁盤檔案嗎?
我們可以反過想,如果每次都是落盤寫入的話實際效率是不高的,無法滿足訊息中間件這種高吞吐的性能要求,因此RocketMQ實際是借助作業系統的page cache來提升寫入效率的,訊息并不是直接寫入磁盤,認識先寫入作業系統的page cache,然后再通過異步刷盤的方式,寫入CommitLog檔案中,這樣借助順序寫以及系統的page cache可以時間近乎記憶體的資料寫入效率,
同步刷盤和異步刷盤
剛才所說的異步寫入,其實就是RocketMQ的異步刷盤模式,但是大家想想這個模式有沒有什么問題,為了提升資料吞吐量,訊息資料過來后,并沒有直接寫盤,而是在系統中的page cache中,那么此時如果Broker宕機了,那么此時的訊息資料是容易丟失的,所以雖然異步刷盤的寫入效率高但是也存在資料丟失的風險,
同步刷盤
在同步刷盤的場景下,當Broker接受到對應的訊息之后,Broker將會把這條訊息刷入磁盤的CommitLog中,才會回傳確認訊息給生產者,如果在進行訊息寫入的時候Broker掛了,那么生產者會感受到訊息投遞失敗,一般都會都有訊息重新發送的重試邏輯,
這樣看訊息似乎不會丟失了,但是由于每次都是先落盤,就會導致資料寫入性能下降,
MMAP
在RocketMQ中使用了mmap技術來實作Conmmitlog檔案的高性能讀寫,mmap就是一種記憶體映射檔案的方法,對于傳統的檔案IO互動來說,需要經過多次的資料復制程序才能將用戶行程的資料寫入硬碟或者讀入程式,而mmap可以直接將虛擬記憶體中的檔案與硬碟中檔案地址進行映射,減少了資料拷貝的程序,從而提升了資料寫入的效率,關于這部分的內容可以參見以前的文章有詳細的介紹,
RocketMQ高性能讀寫

總結
本文主要堆RocketMQ的存盤設計進行了分析,圍繞如何實作高性能訊息寫入和查詢展開了闡述,希望在分析這些優秀中間的具體實作程序中,我們可以將這些優秀設計融入到具體的專案實踐中,當我們遇到類似的問題的時候可以借助于這些設計思想來解決實際的問題,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/356761.html
標籤:其他
