RocketMQ之消息的存儲

1、MQ消息消息存儲的選擇

1.1、消息存儲方式

主要分爲以下幾種:

  • 分佈式KV存儲: 比如ActiveMQ中採用的levelDB、Redis, 這種存儲方式對於消息讀寫能力要求不高的情況下可以使用

  • 文件存儲系統: 常見的比如kafka、RocketMQ、RabbitMQ都是採用消息刷盤到所部署的機器上的文件系統來做持久化,這種方案適合對於有高吞吐量要求的消息中間件,因爲消息刷盤是一種高效率,高可靠、高性能的持久化方式,除非磁盤出現故障,否則一般是不會出現無法持久化的問題

  • 關係型數據庫: 比如ActiveMQ可以採用mysql作爲消息存儲,關係型數據庫在單表數據量達到千萬級的情況下IO性能會出現瓶頸,所以ActiveMQ並不適合於高吞吐量的消息隊列場景。

總的來說,對於存儲效率,文件系統要優於分佈式KV存儲,分佈式KV存儲要優於關係型數據庫

1.2、RocketMQ選擇文件存儲系統方式

RocketMQ作爲一款高性能高可靠的消息中間件,高可靠那就要求對消息進行持久化,就要寫入磁盤,而高性能又與其矛盾,那麼RocketMQ是如何做到的呢?

通過消息順序寫隨機讀的策略,實際上磁盤順序寫是非常快的。

2、RocketMQ存儲結構

2.1、存儲文件介紹

默認存儲路徑爲:/root/store


介紹下各個文件:

  • commitlog: 消息真正的物理存儲文件,所有消息都存儲在CommitLog文件中。每個broker上的commitlog爲當前機器上的所有consumerQueue共享,不做任何分區。

    commitLog中文件默認大小爲1G,可以動態配置。當一個文件寫滿後,會生成一個新的,所有topic數據是順序寫入在commitLog文件中的。文件名長度爲20位,左邊補0,剩餘爲起始偏移量,比如00000000000000000000爲第一個文件,00000000001073741824是第二個文件,1073741824是起始偏移量。

  • consumequeue: 表示消息消費的邏輯隊列,類似於數據庫的索引文件。裏面包含MessageQueue在commitlog中的物理位置偏移量offset,消息內容的大小和Message Tag的hash值。對於實際物理存儲來說,consumeQueue對應每個topic和queueId下的文件,每個文件默認大小約爲600w個子節,如果文件滿後會生成新的文件。



爲什麼要consumequeue?

CommitLog文件是存放消息數據的地方,所有的消息都將存入到CommitLog文件中。生產者將消息發送到 RocketMQ的Broker後,Broker服務器會將消息順序寫入到CommitLog文件中,這也就是RocketMQ高性能的原因,因爲我們知道磁盤順序寫特別快,RocketMQ 充分利用了這一點,極大的提高消息寫入效率。但是消費者消費消息的時候,可能就會遇到麻煩,每一個消費者只能訂閱一個主題,消費者關心的是訂閱主題下的所有消息,但是同一主題的消息在CommitLog文件中可能是不連續的,那麼消費者消費消息的時候,需要將 CommitLog文件加載到內存中遍歷查找訂閱主題下的消息,頻繁的IO操作,性能就會急速下降。爲了解決這個問題,RocketMQ 引入了Consumequeue文件。Consumequeue文件可以看作是索引文件,類似MySQL 中的二級索引。在存放了同一主題下的所有消息,消費者消費的時候只需要去對應的Consumequeue組中取消息即可。Consumequeue文件不會存儲消息的全量信息,瞭解MySQL索引的話,應該好理解這裏。

  • index: 是RocketMQ爲消息訂閱構建的索引文件,用來提高根據主題與消息隊列檢索消息的速度。
  • abort: broker在啓動的時候會創建一個空的abort文件,在shutdown時候刪除,用於標識進程是否正常退出,如果不是,會在啓動時做故障恢復。

3、存儲流程描述

3.1、發送消息,寫入commitlog

producer將消息發送到broker後,broker會採用同步或者異步的方式將消息寫入到commitlog中去,爲保證消息存儲不混亂,寫log之前會加鎖,同時能夠使消息順序寫入到commitlog中去,這裏是順序io,所以速度會非常快。

3.2、寫入consumerqueue

commitlog持久化消息之後,會把裏面的消息dispatch到對應的consumequeue上。

3.3、消費消息

消息消費時候,會先讀取consumerqueue,會拿到消息在commitlog中的物理偏移量offset等信息。

直接從consumerqueue中讀取的消息是沒有數據的,真正的消息在commitlog中,所以還要從commitlog中去讀取消息。

4、聊聊數據刷盤

因爲操作系統PageCache的存在,PageCache是OS對文件的緩存,用於加速對文件的讀寫,所以一般都是先寫入到PageCache中,然後再持久化到磁盤上。我們熟悉的其他組件,MySQL、Redis 等都是如此,RocketMQ 也不列外。在 RocketMQ中提供了同步刷盤異步刷盤兩種刷盤方式,可以通過broker.conf配置中的flushDiskType參數來設置(SYNC_FLUSHASYNC_FLUSH)。

  • 異步刷盤方式(默認):消息寫入到內存的PageCache中,就立刻給客戶端返回寫操作成功,當PageCache中的消息積累到一定的量時,觸發一次寫操作,將PageCache中的消息寫入到磁盤中。這種方式吞吐量大,性能高,但是PageCache中的數據可能丟失,不能保證數據絕對的安全。
  • 同步刷盤方式:消息寫入內存的PageCache後,立刻通知刷盤線程刷盤,然後等待刷盤完成,刷盤線程執行完成後喚醒等待的線程,返回消息寫成功的狀態。這種方式可以保證數據絕對安全,但是吞吐量不大。

5、清除存儲文件時機

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章