RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)-創(chuàng)新互聯(lián)

說在前面

RocketMQ在底層存儲(chǔ)上借鑒了Kafka,但是也有它獨(dú)到的設(shè)計(jì),本文主要關(guān)注深刻影響著RocketMQ性能的底層文件存儲(chǔ)結(jié)構(gòu),中間會(huì)穿插一點(diǎn)點(diǎn)Kafka的東西以作為對比。

創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供吳興網(wǎng)站建設(shè)、吳興做網(wǎng)站、吳興網(wǎng)站設(shè)計(jì)、吳興網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、吳興企業(yè)網(wǎng)站模板建站服務(wù),十載吳興做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。例子

Commit Log,一個(gè)文件集合,每個(gè)文件1G大小,存儲(chǔ)滿后存下一個(gè),為了討論方便可以把它當(dāng)成一個(gè)文件,所有消息內(nèi)容全部持久化到這個(gè)文件中;Consume Queue:一個(gè)Topic可以有多個(gè),每一個(gè)文件代表一個(gè)邏輯隊(duì)列,這里存放消息在Commit Log的偏移值以及大小和Tag屬性。

為了簡述方便,來個(gè)例子

假如集群有一個(gè)Broker,Topic為binlog的隊(duì)列(Consume Queue)數(shù)量為4,如下圖所示,按順序發(fā)送這5條內(nèi)容各不相同消息。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

先簡單關(guān)注下Commit Log和Consume Queue。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

RMQ的消息整體是有序的,所以這5條消息按順序?qū)?nèi)容持久化在Commit Log中。Consume Queue則用于將消息均衡地排列在不同的邏輯隊(duì)列,集群模式下多個(gè)消費(fèi)者就可以并行消費(fèi)Consume Queue的消息。

Page Cache

了解了每個(gè)文件都在什么位置存放什么內(nèi)容,那接下來就正式開始討論這種存儲(chǔ)方案為什么在性能帶來的提升。

通常文件讀寫比較慢,如果對文件進(jìn)行順序讀寫,速度幾乎是接近于內(nèi)存的隨機(jī)讀寫,為什么會(huì)這么快,原因就是Page Cache。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

先來個(gè)直觀的感受,整個(gè)OS有3.7G的物理內(nèi)存,用掉了2.7G,應(yīng)當(dāng)還剩下1G空閑的內(nèi)存,但OS給出的卻是175M。當(dāng)然這個(gè)數(shù)學(xué)題肯定不能這么算。

OS發(fā)現(xiàn)系統(tǒng)的物理內(nèi)存有大量剩余時(shí),為了提高IO的性能,就會(huì)使用多余的內(nèi)存當(dāng)做文件緩存,也就是圖上的buff / cache,廣義我們說的Page Cache就是這些內(nèi)存的子集。

OS在讀磁盤時(shí)會(huì)將當(dāng)前區(qū)域的內(nèi)容全部讀到Cache中,以便下次讀時(shí)能命中Cache,寫磁盤時(shí)直接寫到Cache中就寫返回,由OS的pdflush以某些策略將Cache的數(shù)據(jù)Flush回磁盤。

但是系統(tǒng)上文件非常多,即使是多余的Page Cache也是非常寶貴的資源,OS不可能將Page Cache隨機(jī)分配給任何文件,Linux底層就提供了mmap將一個(gè)程序指定的文件映射進(jìn)虛擬內(nèi)存(Virtual Memory),對文件的讀寫就變成了對內(nèi)存的讀寫,能充分利用Page Cache。不過,文件IO僅僅用到了Page Cache還是不夠的,如果對文件進(jìn)行隨機(jī)讀寫,會(huì)使虛擬內(nèi)存產(chǎn)生很多缺頁(Page Fault)中斷。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

每個(gè)用戶空間的進(jìn)程都有自己的虛擬內(nèi)存,每個(gè)進(jìn)程都認(rèn)為自己所有的物理內(nèi)存,但虛擬內(nèi)存只是邏輯上的內(nèi)存,要想訪問內(nèi)存的數(shù)據(jù),還得通過內(nèi)存管理單元(MMU)查找頁表,將虛擬內(nèi)存映射成物理內(nèi)存。如果映射的文件非常大,程序訪問局部映射不到物理內(nèi)存的虛擬內(nèi)存時(shí),產(chǎn)生缺頁中斷,OS需要讀寫磁盤文件的真實(shí)數(shù)據(jù)再加載到內(nèi)存。如同我們的應(yīng)用程序沒有Cache住某塊數(shù)據(jù),直接訪問數(shù)據(jù)庫要數(shù)據(jù)再把結(jié)果寫到Cache一樣,這個(gè)過程相對而言是非常慢的。

但是順序IO時(shí),讀和寫的區(qū)域都是被OS智能Cache過的熱點(diǎn)區(qū)域,不會(huì)產(chǎn)生大量缺頁中斷,文件的IO幾乎等同于內(nèi)存的IO,性能當(dāng)然就上去了。

說了這么多Page Cache的優(yōu)點(diǎn),也得稍微提一下它的缺點(diǎn),內(nèi)核把可用的內(nèi)存分配給Page Cache后,free的內(nèi)存相對就會(huì)變少,如果程序有新的內(nèi)存分配需求或者缺頁中斷,恰好free的內(nèi)存不夠,內(nèi)核還需要花費(fèi)一點(diǎn)時(shí)間將熱度低的Page Cache的內(nèi)存回收掉,對性能非常苛刻的系統(tǒng)會(huì)產(chǎn)生毛刺。

刷盤

刷盤一般分成:同步刷盤和異步刷盤

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

同步刷盤

在消息真正落盤后,才返回成功給Producer,只要磁盤沒有損壞,消息就不會(huì)丟。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

一般只用于金融場景,這種方式不是本文討論的重點(diǎn),因?yàn)闆]有利用Page Cache的特點(diǎn),RMQ采用GroupCommit的方式對同步刷盤進(jìn)行了優(yōu)化。

異步刷盤

讀寫文件充分利用了Page Cache,即寫入Page Cache就返回成功給Producer,RMQ中有兩種方式進(jìn)行異步刷盤,整體原理是一樣的。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

刷盤由程序和OS共同控制

先談?wù)凮S,當(dāng)程序順序?qū)懳募r(shí),首先寫到Cache中,這部分被修改過,但卻沒有被刷進(jìn)磁盤,產(chǎn)生了不一致,這些不一致的內(nèi)存叫做臟頁(Dirty Page)。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

臟頁設(shè)置太小,F(xiàn)lush磁盤的次數(shù)就會(huì)增加,性能會(huì)下降;臟頁設(shè)置太大,性能會(huì)提高,但萬一OS宕機(jī),臟頁來不及刷盤,消息就丟了。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

一般不是高配玩家,用OS的默認(rèn)值就好,如上圖。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

RMQ想要性能高,那發(fā)送消息時(shí),消息要寫進(jìn)Page Cache而不是直接寫磁盤,接收消息時(shí),消息要從Page Cache直接獲取而不是缺頁從磁盤讀取。

好了,原理回顧完,從消息發(fā)送和消息接收來看RMQ中被mmap后的Commit Log和Consume Queue的IO情況。

RMQ發(fā)送邏輯

發(fā)送時(shí),Producer不直接與Consume Queue打交道。上文提到過,RMQ所有的消息都會(huì)存放在Commit Log中,為了使消息存儲(chǔ)不發(fā)生混亂,對Commit Log進(jìn)行寫之前就會(huì)上鎖。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

消息持久被鎖串行化后,對Commit Log就是順序?qū)?,也就是常說的Append操作。配合上Page Cache,RMQ在寫Commit Log時(shí)效率會(huì)非常高。

Commit Log持久后,會(huì)將里面的數(shù)據(jù)Dispatch到對應(yīng)的Consume Queue上。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

每一個(gè)Consume Queue代表一個(gè)邏輯隊(duì)列,是由ReputMessageService在單個(gè)Thread Loop中Append,顯然也是順序?qū)憽?/p>消費(fèi)邏輯底層

消費(fèi)時(shí),Consumer不直接與Commit Log打交道,而是從Consume Queue中去拉取數(shù)據(jù)

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

拉取的順序從舊到新,在文件表示每一個(gè)Consume Queue都是順序讀,充分利用了Page Cache。

光拉取Consume Queue是沒有數(shù)據(jù)的,里面只有一個(gè)對Commit Log的引用,所以再次拉取Commit Log。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

Commit Log會(huì)進(jìn)行隨機(jī)讀

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

但整個(gè)RMQ只有一個(gè)Commit Log,雖然是隨機(jī)讀,但整體還是有序地讀,只要那整塊區(qū)域還在Page Cache的范圍內(nèi),還是可以充分利用Page Cache。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

在一臺(tái)真實(shí)的MQ上查看網(wǎng)絡(luò)和磁盤,即使消息端一直從MQ讀取消息,也幾乎看不到進(jìn)程從磁盤拉數(shù)據(jù),數(shù)據(jù)直接從Page Cache經(jīng)由Socket發(fā)送給了Consumer。

對比Kafka

文章開頭就說到,RMQ是借鑒了Kafka的想法,同時(shí)也打破了Kafka在底層存儲(chǔ)的設(shè)計(jì)。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

Kafka中關(guān)于消息的存儲(chǔ)只有一種文件,叫做Partition(不考慮細(xì)化的Segment),它履行了RMQ中Commit Log和Consume Queue公共的職責(zé),即它在邏輯上進(jìn)行拆分存,以提高消費(fèi)并行度,又在內(nèi)部存儲(chǔ)了真實(shí)的消息內(nèi)容。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

這樣看上去非常完美,不管對于Producer還是Consumer,單個(gè)Partition文件在正常的發(fā)送和消費(fèi)邏輯中都是順序IO,充分利用Page Cache帶來的巨大性能提升,但是,萬一Topic很多,每個(gè)Topic又分了N個(gè)Partition,這時(shí)對于OS來說,這么多文件的順序讀寫在并發(fā)時(shí)變成了隨機(jī)讀寫。

RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

這時(shí),不知道為什么,我突然想起了「打地鼠」這款游戲。對于每一個(gè)洞,我打的地鼠總是有順序的,但是,萬一有10000個(gè)洞,只有你一個(gè)人去打,無數(shù)只地鼠有先有后的出入于每個(gè)洞,這時(shí)還不是隨機(jī)去打,同學(xué)們腦補(bǔ)下這場景。

當(dāng)然,思路很好的同學(xué)馬上發(fā)現(xiàn)RMQ在隊(duì)列非常多的情況下Consume Queue不也是和Kafka類似,雖然每一個(gè)文件是順序IO,但整體是隨機(jī)IO。不要忘記了,RMQ的Consume Queue是不會(huì)存儲(chǔ)消息的內(nèi)容,任何一個(gè)消息也就占用20 Byte,所以文件可以控制得非常小,絕大部分的訪問還是Page Cache的訪問,而不是磁盤訪問。正式部署也可以將Commit Log和Consume Queue放在不同的物理SSD,避免多類文件進(jìn)行IO競爭。

說在后面

更多精彩的文章,請關(guān)注我的微信公眾號(hào): 艾瑞克的技術(shù)江湖
RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)

本文題目:RocketMQ高性能之底層存儲(chǔ)設(shè)計(jì)-創(chuàng)新互聯(lián)
轉(zhuǎn)載源于:http://muchs.cn/article30/deosso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣網(wǎng)站收錄、網(wǎng)站設(shè)計(jì)公司、品牌網(wǎng)站制作外貿(mào)建站、電子商務(wù)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計(jì)