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

Commit Log,一個文件集合,每個文件1G大小,存儲滿后存下一個,為了討論方便可以把它當成一個文件,所有消息內(nèi)容全部持久化到這個文件中;Consume Queue:一個Topic可以有多個,每一個文件代表一個邏輯隊列,這里存放消息在Commit Log的偏移值以及大小和Tag屬性。
為了簡述方便,來個例子
假如集群有一個Broker,Topic為binlog的隊列(Consume Queue)數(shù)量為4,如下圖所示,按順序發(fā)送這5條內(nèi)容各不相同消息。
先簡單關(guān)注下Commit Log和Consume Queue。
RMQ的消息整體是有序的,所以這5條消息按順序?qū)?nèi)容持久化在Commit Log中。Consume Queue則用于將消息均衡地排列在不同的邏輯隊列,集群模式下多個消費者就可以并行消費Consume Queue的消息。
Page Cache了解了每個文件都在什么位置存放什么內(nèi)容,那接下來就正式開始討論這種存儲方案為什么在性能帶來的提升。
通常文件讀寫比較慢,如果對文件進行順序讀寫,速度幾乎是接近于內(nèi)存的隨機讀寫,為什么會這么快,原因就是Page Cache。
先來個直觀的感受,整個OS有3.7G的物理內(nèi)存,用掉了2.7G,應(yīng)當還剩下1G空閑的內(nèi)存,但OS給出的卻是175M。當然這個數(shù)學(xué)題肯定不能這么算。
OS發(fā)現(xiàn)系統(tǒng)的物理內(nèi)存有大量剩余時,為了提高IO的性能,就會使用多余的內(nèi)存當做文件緩存,也就是圖上的buff / cache,廣義我們說的Page Cache就是這些內(nèi)存的子集。
OS在讀磁盤時會將當前區(qū)域的內(nèi)容全部讀到Cache中,以便下次讀時能命中Cache,寫磁盤時直接寫到Cache中就寫返回,由OS的pdflush以某些策略將Cache的數(shù)據(jù)Flush回磁盤。
但是系統(tǒng)上文件非常多,即使是多余的Page Cache也是非常寶貴的資源,OS不可能將Page Cache隨機分配給任何文件,Linux底層就提供了mmap將一個程序指定的文件映射進虛擬內(nèi)存(Virtual Memory),對文件的讀寫就變成了對內(nèi)存的讀寫,能充分利用Page Cache。不過,文件IO僅僅用到了Page Cache還是不夠的,如果對文件進行隨機讀寫,會使虛擬內(nèi)存產(chǎn)生很多缺頁(Page Fault)中斷。
每個用戶空間的進程都有自己的虛擬內(nèi)存,每個進程都認為自己所有的物理內(nèi)存,但虛擬內(nèi)存只是邏輯上的內(nèi)存,要想訪問內(nèi)存的數(shù)據(jù),還得通過內(nèi)存管理單元(MMU)查找頁表,將虛擬內(nèi)存映射成物理內(nèi)存。如果映射的文件非常大,程序訪問局部映射不到物理內(nèi)存的虛擬內(nèi)存時,產(chǎn)生缺頁中斷,OS需要讀寫磁盤文件的真實數(shù)據(jù)再加載到內(nèi)存。如同我們的應(yīng)用程序沒有Cache住某塊數(shù)據(jù),直接訪問數(shù)據(jù)庫要數(shù)據(jù)再把結(jié)果寫到Cache一樣,這個過程相對而言是非常慢的。
但是順序IO時,讀和寫的區(qū)域都是被OS智能Cache過的熱點區(qū)域,不會產(chǎn)生大量缺頁中斷,文件的IO幾乎等同于內(nèi)存的IO,性能當然就上去了。
說了這么多Page Cache的優(yōu)點,也得稍微提一下它的缺點,內(nèi)核把可用的內(nèi)存分配給Page Cache后,free的內(nèi)存相對就會變少,如果程序有新的內(nèi)存分配需求或者缺頁中斷,恰好free的內(nèi)存不夠,內(nèi)核還需要花費一點時間將熱度低的Page Cache的內(nèi)存回收掉,對性能非常苛刻的系統(tǒng)會產(chǎn)生毛刺。
刷盤刷盤一般分成:同步刷盤和異步刷盤
同步刷盤
在消息真正落盤后,才返回成功給Producer,只要磁盤沒有損壞,消息就不會丟。
一般只用于金融場景,這種方式不是本文討論的重點,因為沒有利用Page Cache的特點,RMQ采用GroupCommit的方式對同步刷盤進行了優(yōu)化。
異步刷盤
讀寫文件充分利用了Page Cache,即寫入Page Cache就返回成功給Producer,RMQ中有兩種方式進行異步刷盤,整體原理是一樣的。
刷盤由程序和OS共同控制
先談?wù)凮S,當程序順序?qū)懳募r,首先寫到Cache中,這部分被修改過,但卻沒有被刷進磁盤,產(chǎn)生了不一致,這些不一致的內(nèi)存叫做臟頁(Dirty Page)。
臟頁設(shè)置太小,F(xiàn)lush磁盤的次數(shù)就會增加,性能會下降;臟頁設(shè)置太大,性能會提高,但萬一OS宕機,臟頁來不及刷盤,消息就丟了。
一般不是高配玩家,用OS的默認值就好,如上圖。
RMQ想要性能高,那發(fā)送消息時,消息要寫進Page Cache而不是直接寫磁盤,接收消息時,消息要從Page Cache直接獲取而不是缺頁從磁盤讀取。
好了,原理回顧完,從消息發(fā)送和消息接收來看RMQ中被mmap后的Commit Log和Consume Queue的IO情況。
RMQ發(fā)送邏輯發(fā)送時,Producer不直接與Consume Queue打交道。上文提到過,RMQ所有的消息都會存放在Commit Log中,為了使消息存儲不發(fā)生混亂,對Commit Log進行寫之前就會上鎖。
消息持久被鎖串行化后,對Commit Log就是順序?qū)懀簿褪浅Uf的Append操作。配合上Page Cache,RMQ在寫Commit Log時效率會非常高。
Commit Log持久后,會將里面的數(shù)據(jù)Dispatch到對應(yīng)的Consume Queue上。
每一個Consume Queue代表一個邏輯隊列,是由ReputMessageService在單個Thread Loop中Append,顯然也是順序?qū)憽?/p>消費邏輯底層
消費時,Consumer不直接與Commit Log打交道,而是從Consume Queue中去拉取數(shù)據(jù)
拉取的順序從舊到新,在文件表示每一個Consume Queue都是順序讀,充分利用了Page Cache。
光拉取Consume Queue是沒有數(shù)據(jù)的,里面只有一個對Commit Log的引用,所以再次拉取Commit Log。
Commit Log會進行隨機讀
但整個RMQ只有一個Commit Log,雖然是隨機讀,但整體還是有序地讀,只要那整塊區(qū)域還在Page Cache的范圍內(nèi),還是可以充分利用Page Cache。
在一臺真實的MQ上查看網(wǎng)絡(luò)和磁盤,即使消息端一直從MQ讀取消息,也幾乎看不到進程從磁盤拉數(shù)據(jù),數(shù)據(jù)直接從Page Cache經(jīng)由Socket發(fā)送給了Consumer。
對比Kafka文章開頭就說到,RMQ是借鑒了Kafka的想法,同時也打破了Kafka在底層存儲的設(shè)計。
Kafka中關(guān)于消息的存儲只有一種文件,叫做Partition(不考慮細化的Segment),它履行了RMQ中Commit Log和Consume Queue公共的職責,即它在邏輯上進行拆分存,以提高消費并行度,又在內(nèi)部存儲了真實的消息內(nèi)容。
這樣看上去非常完美,不管對于Producer還是Consumer,單個Partition文件在正常的發(fā)送和消費邏輯中都是順序IO,充分利用Page Cache帶來的巨大性能提升,但是,萬一Topic很多,每個Topic又分了N個Partition,這時對于OS來說,這么多文件的順序讀寫在并發(fā)時變成了隨機讀寫。
這時,不知道為什么,我突然想起了「打地鼠」這款游戲。對于每一個洞,我打的地鼠總是有順序的,但是,萬一有10000個洞,只有你一個人去打,無數(shù)只地鼠有先有后的出入于每個洞,這時還不是隨機去打,同學(xué)們腦補下這場景。
當然,思路很好的同學(xué)馬上發(fā)現(xiàn)RMQ在隊列非常多的情況下Consume Queue不也是和Kafka類似,雖然每一個文件是順序IO,但整體是隨機IO。不要忘記了,RMQ的Consume Queue是不會存儲消息的內(nèi)容,任何一個消息也就占用20 Byte,所以文件可以控制得非常小,絕大部分的訪問還是Page Cache的訪問,而不是磁盤訪問。正式部署也可以將Commit Log和Consume Queue放在不同的物理SSD,避免多類文件進行IO競爭。
說在后面更多精彩的文章,請關(guān)注我的微信公眾號: 艾瑞克的技術(shù)江湖
本文題目:RocketMQ高性能之底層存儲設(shè)計-創(chuàng)新互聯(lián)
轉(zhuǎn)載源于:http://www.yijiale78.com/article30/deosso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、網(wǎng)站收錄、網(wǎng)站設(shè)計公司、品牌網(wǎng)站制作、外貿(mào)建站、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容