2019-12-14深入研究一下Broker是如何持久化存儲(chǔ)消息的?

1、為什么Broker數(shù)據(jù)存儲(chǔ)是最重要的一個(gè)環(huán)節(jié)?

上次給大家分享完P(guān)roducer的工作原理之后,團(tuán)隊(duì)整體都對(duì)RocketMQ的數(shù)據(jù)分片機(jī)制以及發(fā)送消息的時(shí)候如何寫入各個(gè)Broker機(jī)器有了一定的了解。接著就開始來給大家分享最為重要的Broker數(shù)據(jù)存儲(chǔ)的環(huán)節(jié)。

首先我們得明確一點(diǎn),為什么Broker數(shù)據(jù)存儲(chǔ)是最重要的一個(gè)環(huán)節(jié)?

很簡(jiǎn)單,實(shí)際上類似RocketMQ、Kafka、RabbitMQ的消息中間件系統(tǒng),他們不只是讓你寫入消息和獲取消息那么簡(jiǎn)單,他們本身最重要的就是提供強(qiáng)大的數(shù)據(jù)存儲(chǔ)能力,可以把億萬級(jí)的海量消息存儲(chǔ)在自己的服務(wù)器的磁盤上。

這樣的話,各種不同的系統(tǒng)從MQ中消費(fèi)消息的時(shí)候,才可以從MQ服務(wù)器的磁盤中讀取到自己需要的消息。

否則如果MQ不在機(jī)器磁盤上存儲(chǔ)大量的消息,如果消息都放在自己的內(nèi)存里,一個(gè)是內(nèi)存很可能放不下,另外一個(gè)是可能你機(jī)器重啟,內(nèi)存里的消息就會(huì)全部丟失了。

所以大家首先要明確一點(diǎn),Broker數(shù)據(jù)存儲(chǔ)實(shí)際上才是一個(gè)MQ最核心的環(huán)節(jié),他決定了生產(chǎn)者消息寫入的吞吐量,決定了消息不能丟失,決定了消費(fèi)者獲取消息的吞吐量,這些都是由他決定的。

所以今天我們來深入的探索一下Broker的數(shù)據(jù)存儲(chǔ)機(jī)制。

2、CommitLog消息順序?qū)懭霗C(jī)制

首先我們來思考一下,當(dāng)生產(chǎn)者的消息發(fā)送到一個(gè)Broker上的時(shí)候,他接收到了一條消息,接著他會(huì)對(duì)這個(gè)消息做什么事情?

首先第一步,他會(huì)把這個(gè)消息直接寫入磁盤上的一個(gè)日志文件,叫做CommitLog,直接順序?qū)懭脒@個(gè)文件,如下圖。

image.png

這個(gè)CommitLog是很多磁盤文件,每個(gè)文件限定最多1GB,Broker收到消息之后就直接追加寫入這個(gè)文件的末尾,就跟上面的圖里一樣。如果一個(gè)CommitLog寫滿了1GB,就會(huì)創(chuàng)建一個(gè)新的CommitLog文件。

3、MessageQueue在數(shù)據(jù)存儲(chǔ)中是體現(xiàn)在哪里呢?

接著我們會(huì)發(fā)現(xiàn)一個(gè)問題,如果寫入這個(gè)Broker的消息都是進(jìn)入到CommitLog中去存儲(chǔ)的,那么上次我們提到的MessageQueue是體現(xiàn)在哪里的呢?

其實(shí)在Broker中,對(duì)Topic下的每個(gè)MessageQueue都會(huì)有一系列的ConsumeQueue文件。
這是什么意思呢?

就是在Broker的磁盤上,會(huì)有下面這種格式的一系列文件:

$HOME/store/consumequeue/{topic}/{queueId}/{fileName}

上面那一串東西是什么意思?

我們之前說過,對(duì)每個(gè)Topic你不是在這臺(tái)Broker上都會(huì)有一些MessageQueue嗎?所以你會(huì)看到,{topic}指代的就是某個(gè)Topic,{queueId}指代的就是某個(gè)MessageQueue。
然后對(duì)存儲(chǔ)在這臺(tái)Broker機(jī)器上的Topic下的一個(gè)MessageQueue,他有很多的ConsumeQueue文件,這個(gè)ConsumeQueue文件里存儲(chǔ)的是一條消息對(duì)應(yīng)在CommitLog文件中的offset偏移量。
很多人可能看到這里就直接看暈了,沒明白這個(gè)是什么意思。。。

沒關(guān)系,我們一步一圖來給大家說明一下這是怎么回事。

首先我們假設(shè)有一個(gè)Topic,他有4個(gè)MessageQueue,然后在兩臺(tái)Broker機(jī)器上,每臺(tái)Broker機(jī)器會(huì)存儲(chǔ)兩個(gè)MessageQueue。

那么此時(shí)假設(shè)生產(chǎn)者選擇對(duì)其中一個(gè)MessageQueue寫入了一條消息,此時(shí)消息會(huì)發(fā)送到Broker上。

然后Broker必然會(huì)把這個(gè)消息寫入自己的CommitLog文件中,是不是?
好,我們看下面的圖里,我用紅圈畫出來了一個(gè)消息,我們假設(shè)就是剛剛寫入的消息。


image.png

我們繼續(xù)看下面的圖,我在圖里加入了兩個(gè)ConsumeQueue,分別叫做ConsumeQueue0和ConsumeQueue1,他們分別對(duì)應(yīng)著Topic里的MessageQueue0和MessageQueue1。


image.png

也就是說,Topic下的MessageQueue0和MessageQueue1就放在這個(gè)Broker機(jī)器上,而且他們每個(gè)MessageQueue目前在磁盤上就對(duì)應(yīng)了一個(gè)ConsumeQueue,所以就是MessageQueue0對(duì)應(yīng)著Broker磁盤上的ConsumeQueue0,MessageQueue1對(duì)應(yīng)著磁盤上的ConsumeQueue1。
接著假設(shè)Queue的名字叫做:TopicOrderPaySuccess,那么此時(shí)在Broker磁盤上應(yīng)該有如下兩個(gè)路徑的文件:

$HOME/store/consumequeue/TopicOrderPaySuccess/MessageQueue0/ConsumeQueue0磁盤文件

$HOME/store/consumequeue/TopicOrderPaySuccess/MessageQueue1/ConsumeQueue1磁盤文件
然后呢,當(dāng)你的Broker收到一條消息寫入了CommitLog之后,其實(shí)他同時(shí)會(huì)將這條消息在CommitLog中的物理位置,也就是一個(gè)文件偏移量,就是一個(gè)offset,寫入到這條消息所屬的MessageQueue對(duì)應(yīng)的ConsumeQueue文件中去。
比如現(xiàn)在這條消息在生產(chǎn)者發(fā)送的時(shí)候是發(fā)送給MessageQueue0的,那么此時(shí)Broker就會(huì)將這條消息在CommitLog中的offset偏移量,寫入到MessageQueue0對(duì)應(yīng)的ConsumeQueue0中去,如下圖所示。


image.png

所以實(shí)際上,ConsumeQueue0中存儲(chǔ)的是一個(gè)一個(gè)消息在CommitLog文件中的物理位置,也就是offset

所以其實(shí)大家看下面的圖,圖里展示出來的是ConsumeQueue中的一個(gè)物理位置其實(shí)是對(duì)CommitLog文件中一個(gè)消息的引用


image.png

實(shí)際上在ConsumeQueue中存儲(chǔ)的每條數(shù)據(jù)不只是消息在CommitLog中的offset偏移量,還包含了消息的長(zhǎng)度,以及tag hashcode,一條數(shù)據(jù)是20個(gè)字節(jié),每個(gè)ConsumeQueue文件保存30萬條數(shù)據(jù),大概每個(gè)文件是5.72MB。

所以實(shí)際上Topic的每個(gè)MessageQueue都對(duì)應(yīng)了Broker機(jī)器上的多個(gè)ConsumeQueue文件,保存了這個(gè)MessageQueue的所有消息在CommitLog文件中的物理位置,也就是offset偏移量。

4、如何讓消息寫入CommitLog文件近乎內(nèi)存寫性能的?

接著我們給大家講一個(gè)比較關(guān)鍵的概念:對(duì)于生產(chǎn)者把消息寫入到Broker時(shí),Broker會(huì)直接把消息寫入磁盤上的CommitLog文件,那么Broker是如何提升整個(gè)過程的性能的呢?

因?yàn)檫@個(gè)部分的性能提升會(huì)直接提升Broker處理消息寫入的吞吐量,比如你寫入一條消息到CommitLog磁盤文件假設(shè)需要10ms,那么每個(gè)線程每秒可以處理100個(gè)寫入消息,假設(shè)有100個(gè)線程,每秒只能處理1萬個(gè)寫入消息請(qǐng)求。

但是如果你把消息寫入CommitLog磁盤文件的性能優(yōu)化為只需要1ms,那么每個(gè)線程每秒可以處理1000個(gè)消息寫入,此時(shí)100個(gè)線程每秒可以處理10萬個(gè)寫入消息請(qǐng)求。所以大家可以明顯看到,Broker把接收到的消息寫入CommitLog磁盤文件的性能,對(duì)他的TPS有很大的影響。
所以在這里,Broker是基于OS操作系統(tǒng)的PageCache和順序?qū)憙蓚€(gè)機(jī)制,來提升寫入CommitLog文件的性能的。
首先Broker是以順序的方式將消息寫入CommitLog磁盤文件的,也就是每次寫入就是在文件末尾追加一條數(shù)據(jù)就可以了,對(duì)文件進(jìn)行順序?qū)懙男阅芤葘?duì)文件隨機(jī)寫的性能提升很多。
我們看下面圖里的紅圈,就是示意數(shù)據(jù)是順序?qū)懭氲摹?/p>

image.png

另外,數(shù)據(jù)寫入CommitLog文件的時(shí)候,其實(shí)不是直接寫入底層的物理磁盤文件的,而是先進(jìn)入OS的PageCache內(nèi)存緩存中,然后后續(xù)由OS的后臺(tái)線程選一個(gè)時(shí)間,異步化的將OS PageCache內(nèi)存緩沖中的數(shù)據(jù)刷入底層的磁盤文件。
我們看下面的圖,圖里示意出了,數(shù)據(jù)先寫入OS的PageCache緩存中,然后后續(xù)由OS自己的線程將緩存里的數(shù)據(jù)刷入磁盤中。


image.png

所以在這樣的優(yōu)化之下,采用磁盤文件順序?qū)?OS PageCache寫入+OS異步刷盤的策略,基本上可以讓消息寫入CommitLog的性能跟你直接寫入內(nèi)存里是差不多的,所以正是如此,才可以讓Broker高吞吐的處理每秒大量的消息寫入。

5、同步刷盤與異步刷盤

想必很多朋友此時(shí)可能意識(shí)到一個(gè)問題了,那么如果采用上述的模式,不就是異步刷盤的模式嗎?

對(duì)的,在上述的異步刷盤模式下,生產(chǎn)者把消息發(fā)送給Broker,Broker將消息寫入OS PageCache中,就直接返回ACK給生產(chǎn)者了。

此時(shí)生產(chǎn)者就認(rèn)為消息寫入成功了,那么會(huì)有什么問題嗎?

問題肯定是有的,如果生產(chǎn)者認(rèn)為消息寫入成功了,但是實(shí)際上那條消息此時(shí)是在Broker機(jī)器上的os cache中的,如果此時(shí)Broker直接宕機(jī),那么是不是os cache中的這條數(shù)據(jù)就會(huì)丟失了?

我們看下面的圖,紅圈圈出來了數(shù)據(jù)早os cache里的情況,如果此時(shí)broker宕機(jī),那么必然導(dǎo)致這里的數(shù)據(jù)丟失,而producer還以為數(shù)據(jù)已經(jīng)寫入成功了,以為不會(huì)丟失,所以肯定是有問題的。

所以異步刷盤的的策略下,可以讓消息寫入吞吐量非常高,但是可能會(huì)有數(shù)據(jù)丟失的風(fēng)險(xiǎn),這個(gè)是大家需要清除的。


image.png

另外一種模式叫做同步刷盤,如果你使用同步刷盤模式的話,那么生產(chǎn)者發(fā)送一條消息出去,broker收到了消息,必須直接強(qiáng)制把這個(gè)消息刷入底層的物理磁盤文件中,然后才會(huì)返回ack給producer,此時(shí)你才知道消息寫入成功了。

只要消息進(jìn)入了物理磁盤上,那么除非是你的物理磁盤壞了導(dǎo)致數(shù)據(jù)丟失,否則正常來說數(shù)據(jù)就不會(huì)丟失了,我們看下面的圖,就是示意了同步刷盤的效果。


image.png

如果broker還沒有來得及把數(shù)據(jù)同步刷入磁盤,然后他自己掛了,那么此時(shí)對(duì)producer來說會(huì)感知到消息發(fā)送失敗了,然后你只要不停的重試發(fā)送就可以了,直到有slave broker切換成master broker重新讓你可以寫入消息,此時(shí)可以保證數(shù)據(jù)是不會(huì)丟的。

但是如果你強(qiáng)制每次消息寫入都要直接進(jìn)入磁盤中,必然導(dǎo)致每條消息寫入性能急劇下降,導(dǎo)致消息寫入吞吐量急劇下降,但是可以保證數(shù)據(jù)不會(huì)丟失。

好了,今天主要是分析一下broker對(duì)數(shù)據(jù)是如何存儲(chǔ)的,從原理角度帶著大家一步一圖來分析一下,只有具體如何切換異步刷盤和同步刷盤的一些配置,后續(xù)我們會(huì)結(jié)合業(yè)務(wù)場(chǎng)景下的數(shù)據(jù)0丟失方案來講解的。

6、對(duì)今天內(nèi)容的一點(diǎn)小小總結(jié)

今天我們講了broker最為核心的數(shù)據(jù)存儲(chǔ)機(jī)制,包括如下一些知識(shí)點(diǎn):

為什么Broker數(shù)據(jù)存儲(chǔ)機(jī)制是一個(gè)MQ最為核心的環(huán)節(jié)?

CommitLog數(shù)據(jù)存儲(chǔ)機(jī)制

MessageQueue對(duì)應(yīng)的ConsumeQueue物理位置存儲(chǔ)機(jī)制

基于CommitLog順序?qū)?OS Cache+異步刷盤的高吞吐消息寫入的機(jī)制

同步刷盤和異步刷盤各自的優(yōu)缺點(diǎn):高吞吐寫入+丟失數(shù)據(jù)風(fēng)險(xiǎn),寫入吞吐量下降+數(shù)據(jù)不丟失

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、為了隨時(shí)準(zhǔn)備應(yīng)對(duì)線上系統(tǒng)的問題,要深入研究MQ 線上生產(chǎn)環(huán)境部署架構(gòu)圖: 目前公司生產(chǎn)環(huán)境的情況,就是部署了一...
    墨_9d2e閱讀 721評(píng)論 2 0
  • RocketMQ消息存儲(chǔ) 1 CommitLog 要想知道RocketMQ如何存儲(chǔ)消息,我們先看看CommitLo...
    無醉_1866閱讀 843評(píng)論 0 2
  • 消息存儲(chǔ)部分是RocketMQ的重要組成部分,良好的存儲(chǔ)機(jī)制會(huì)有效降低延遲,提高整體效率。RocketMQ利用到了...
    九點(diǎn)半的馬拉閱讀 486評(píng)論 0 1
  • 今天去兩個(gè)縣督導(dǎo)工作,即便辛苦,可是我還是快樂的,我珍惜這樣到處走走看看的日子,和不同的人聊天,聽他們表達(dá),看見他...
    e517b207832d閱讀 467評(píng)論 1 4
  • 時(shí)間是賽跑的啞巴 走過 溜過 卻從未提醒過 等待是靜止的聾子 望過 盼過 卻從未遇見過 總以為等待漫長(zhǎng) 轉(zhuǎn)身卻已錯(cuò)...
    純瑟閱讀 356評(píng)論 12 3

友情鏈接更多精彩內(nèi)容