再談QMQ與RocketMQ存儲結(jié)構(gòu)

<meta charset="utf-8">

之前做組內(nèi)分享的準(zhǔn)備的時候,我簡單的寫了一些RocketMQ與QMQ存儲結(jié)構(gòu)上的差異(http://www.itdecent.cn/p/c2e2e9deb699),當(dāng)時寫的不是很詳細(xì),也存在一些不太準(zhǔn)確的表述。我想重新用一篇文章,再理一理這里邊的差異。

先講更廣為人知的RocketMQ,我先貼一張基礎(chǔ)的存儲結(jié)構(gòu)

3.png

RocketMQ存儲結(jié)構(gòu)可以概括為三大部分:CommitLog,ConsumerQueue和IndexFile。

CommitLog是所有消息的順序存儲文件,broker收到的消息無腦往里邊append。文件位于${ROCKET_HOME}/store/commitlog目錄下,每個文件默認(rèn)大小1G,超過就新建一個。所以commitLog并不是一個單一文件,而是一組文件的統(tǒng)稱,文件名就是該文件的第一個偏移量。

在RocketMQ的源碼中,commitlog目錄對應(yīng)的邏輯對象叫做MappedFileQueue,而具體的以起始偏移量命名的文件對應(yīng)的邏輯對象叫做MappedFile。在消息寫入commitlog文件之前,線程會先申請鎖,因此消息寫入commitlog的串行的。

ConsumerQueue眾所周知,是commitlog的索引文件,根據(jù)topic來對消息區(qū)分存儲,當(dāng)然為了節(jié)約空間,ConsumerQueue文件里并不會存儲消息的具體內(nèi)容,從圖中我們可以看到,commitlog在消息寫入之后,由專門的線程產(chǎn)生消息轉(zhuǎn)發(fā)任務(wù),同步到ConsumerQueue中的只有commitlog offset,size,tag hashcode這三個信息。ConsumerQueue文件并不是像commitLog一樣文件直接堆放在一起,而是在consumerQueue目錄下,根據(jù)topic/queueId,再分了兩層目錄,提升了檢索的效率。

IndexFile顧名思義就是一個索引文件,保存了消息的key值+消息在commitlog中的偏移量的鍵值對,便于通過消息的Key直接檢索到消息的內(nèi)容。IndexFile由三個部分組成,分別是總長為固定40個字節(jié)的IndexHeader,500w個Hash槽,還有2000w個Index條目,hash槽可以理解為HashMap中的數(shù)組,而index條目是用來解決hash沖突的,可以理解為Java HashMap中的鏈表。

總結(jié)來說,commitlog是一個大倉庫,完成基本的存儲,consumerQueue是針對消費(fèi)者而設(shè)計的,而IndexFile是為了檢索而設(shè)計的,側(cè)重點(diǎn)不同。

CommitLog在構(gòu)建ConsumerQueue是通過ReputMessageService異步實(shí)現(xiàn)的。從buffer中一條一條的讀消息,讀到以后根據(jù)消息的屬性獲取對應(yīng)的ConsumerQueue文件,然后寫進(jìn)去。也就是說,從commitLog中讀出來的消息就自己帶了queueId,這樣也比較好理解,consumerQueue由消息發(fā)送方指定的話,才有可能實(shí)現(xiàn)順序消息。

說完了RocketQMQ的存儲設(shè)計,再來聊聊QMQ的。QMQ的存儲設(shè)計借鑒了RocketMQ和Kafka,并在他們的基礎(chǔ)上做了自己的一些優(yōu)化。

QMQ有類似于RocketMQ的commitlog的存儲結(jié)構(gòu),叫messageLog,也是用來順序存儲消息的。每個文件的大小也是1G,基本的屬性和RocketMQ都一致。

消息在寫入messageLog后,起一個異步線程構(gòu)建一個數(shù)據(jù)結(jié)構(gòu)叫smt,全稱為sorted messages table,顧名思義,這個smt不是對標(biāo)ConsumerQueue的,它把所有的消息重新排序,按照subject排在一起。subject就是RocketMQ里的topic。這么做是為了在消息堆積時,提高page cache的命中率,使得消息消費(fèi)時減少磁盤的IO。同時,smt是專門用來讀的,等于對messageLog做了一個讀寫分離。這么做還有一個好處,就是消息內(nèi)容在回寫完smt之后,就可以將messageLog中的消息刪除,節(jié)約了messageLog的磁盤占用,常規(guī)情況下messageLog就不會承擔(dān)太多的消息存儲,可以直接上SSD,提升消息寫的IO性能。

consumerLog,將smt中排序好的消息,根據(jù)subject分發(fā)到各個文件里。文件中存儲的是消息在smt中的索引。smt與consumerLog的內(nèi)容寫入是一起進(jìn)行的。

pullLog文件是消費(fèi)者的消費(fèi)記錄文件,以subject+consumerGroup+consumerId為維度,保留了每一個消費(fèi)者的歷史消費(fèi)記錄,這樣可以在消費(fèi)者重啟之后快速定位到之前消費(fèi)的位置。

4.png

其實(shí)可以理解為,consumer log + pull log相當(dāng)于替代了RocketMQ中的COnsumerQueue的作用,將其做了一個拆分。我們知道在系統(tǒng)架構(gòu)中,加一個中間層的好處和缺點(diǎn)都是很明顯的,好處就是動態(tài)擴(kuò)展會更加的靈活,耦合性會更加的低。同時,文件多了效率自然也就會收到影響,步驟越多 出錯的機(jī)率越高。

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

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