RocketMQ在儲存架構上的極致追求

內容導讀:MQ作為一款中介軟體,就需要承載全公司所有業務系統使用需求,並高效穩定執行。因此,MQ對本身執行效率有著非常苛刻的訴求。

為了實現高效率,其實需要很多方面一起配合來完成。比如儲存方式、記憶體使用、負載均衡等等。

本文就RocketMQ為了實現高效的讀寫速率在儲存架構上所做的努力,進行下闡述。

Part one / 儲存結構選型對比

為了更方便的進行資料讀寫,訊息在磁碟底層的檔案目錄設計,都需要關注和解決什麼問題呢:

首先,最基本的,訊息原始記錄的寫入和儲存,且速率要快。 其次,要可以區分 ,特別是允許消費者按進行接收。 再次,分散式叢集下的多消費者負載均衡。

那麼問題來了,訊息檔案該怎麼設計呢?

如果按來拆分檔案進行儲存,是否可以?

缺點:生產者寫入時選擇對應的檔案來寫入。當資料量逐漸增大之後,定位查詢檔案地址,對磁碟的定址所帶來的效能損耗,將不再可以忽略。 優點:在消費時,可以直接載入相關檔案進行讀取,不會產生隨機定址。

如果用一整個檔案來存訊息呢?

優點:所有的都被寫入一個檔案中,這樣,寫入時,只要將訊息按到達順序序追加到檔案尾部即可,很容易實現順序寫入。 缺點:消費時,需要根據輔助資訊來在檔案中定位訊息,會產生隨機讀,損耗效能。

因此,不管是按拆開多檔案儲存,還是一整個檔案儲存做有利有弊,需要按實際需要進行權衡。

Part two / RocketMQ的儲存方案選擇

儲存原始訊息選擇的是寫同一個檔案。

RocketMQ在儲存架構上的極致追求

生產者將訊息順序寫入commitLog檔案

究其原因,是由於一般都是普通業務場景使用居多,生產者和眾多,如果都獨立開各自儲存,每次訊息生產的磁碟定址對效能損耗是非常巨大的。

旁證側引:

的檔案儲存方式,是按拆分成來進行的。是什麼樣的原因,讓做出了和相反的選擇呢?

個人認為,主要還是使用場景的區別,被優先選擇用來進行大資料處理,相對於業務場景,資料維度的要少很多,並且的生產者( 等)機器會更加集中,這使得選擇按拆分檔案的缺陷不那麼突出,而大資料處理更重要的是訊息讀取,順序讀的優勢得以被充分利用。

RocketMQ在儲存架構上的極致追求

“單,單的,效能異常的優秀” 是經常被提及的一個觀點,其原因,相信有了上面的分析應該也差不多有結論了。

Part three / RocketMQ怎樣平衡讀效能

從第一部分的儲存方案對比可以知道,為了保證訊息寫入效率,在儲存結構上選擇了,勢必會對訊息的讀取和消費帶來不便。

那麼,它是怎麼來平衡消費時的讀取速率的呢?

關鍵問題是,找到一種途徑,可以快速的在中定位到所需訊息的位置。

從一堆資料中,快速定位想要的資料,這不是最擅長的事情麼?所以,也為建立了,並且是區分的結構。

RocketMQ在儲存架構上的極致追求

儲存架構和儲存構建鏈路示意圖RocketMQ 的訊息體構成訊息體元素構成 是業務場景的唯一標識,不可缺少; 在申請topic的時候確定,關聯著消費索引中的佇列ID; 是訊息特殊標籤,用於業務系統訂閱時提前過濾(這個功能真的是太重要了,吃過苦的同學都清楚); 是訊息的關鍵字,構建index索引,用於關鍵字查詢用; 是真實訊息體;

訊息由釋出者釋出,並依次的、順序的寫到裡,訊息一旦被寫入,是不可以更改順序和內容的。規定最大1個G,達到規定大小則寫新的一個檔案。

索引結構和構建過程

RocketMQ在儲存架構上的極致追求

consumerQueue結構和建立過程

是一種機制,可以讓消費端透過和之間的檢索關係,快速定位到裡邊的具體訊息內容,然後拉取進行消費。

按 的不同,被分為不同的,根據來被消費者訂閱和消費;

其中每個索引項是一個固定大小為20的記錄,由訊息在中的起始偏移量、訊息體佔用大小、的三部分構成。可以透過這三個部分快速定位到所需訊息位置和型別。

而上述索引的構建過程,是在訊息被寫入時,專門的後臺服務——,將索引資訊分發到 consumerQueue 和index檔案裡,來構建索引項。

建索引的過程,實際上是一種分而治之思維的落地,除了索引,還有redis中的各種指標維護,核心是 分散壓力到每次請求,避免了大規模集中計算。

訊息的消費

消費者對應consumerQueue不一定是一對一的,因此,怎麼來讓每個新的消費者來了不會重複消費呢?

RocketMQ在儲存架構上的極致追求

offset消費位點記錄

在訊息成功被拉取並消費時,後臺任務 會將當前消費者,針對topic的消費位點進行記錄,目的是讓下一個或者重新啟動單餓消費者記住這個消費位點,不至於重複消費。

因此,整個檔案目錄就一目瞭然了:

RocketMQ在儲存架構上的極致追求

Part four / 讀效率的追求

雖然透過上述檔案儲存結構的分析,我們知道,消費者可以根據索引檔案中的索引項來快速定位, 但事實上,訊息的釋出和消費,不可能直接針對磁碟進行讀寫操作的,這樣效率會非常非常低。

實際上,我們的操作基本是針對一塊記憶體進行操作的

利用NIO的記憶體對映機制,我們將的一部分檔案交換到對外記憶體。然後利用的技術,在執行過程中把記憶體裡的資訊,與磁盤裡的檔案資訊進行同步,或者交換:

訊息釋出者,在釋出訊息的時候,首先把訊息新增到記憶體裡,然後根據刷盤的配置可以來指定是同步刷盤還是非同步刷盤,來將記憶體中的資料同步到磁碟上。 訊息的消費者,在消費訊息的時候,大多數情況下,會直接命中到記憶體上,不會進行磁碟讀,但極個別的情況下,需要消費的訊息,在記憶體中沒法找到,這時候,就需要用換頁技術,將相關的資訊,拉取到記憶體中。為什麼是相關資訊,而不是需要什麼拉取什麼?這是有一個機制,來保證潛在的即將被消費的資訊直接換入記憶體,來提交效率。

RocketMQ在儲存架構上的極致追求

摘自:Qcon大會 RocketMQ分享資料Part five / 總結

整體一套處理流程看下來,其實我們可以看到很多熟悉的身影,比如Mysql的索引,redis的統計資訊記錄等等,都非常相似。

其實,我們可以這麼認為:對於資訊儲存和查詢的處理方案大都如出一轍,只要把握住最核心的部分,然後根據實際業務訴求進行適配最佳化,基本都是可以達到期望的結果的。

分享、點贊、在看

,給個

3連擊

TAG: 訊息儲存檔案索引記憶體