當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]大家好,我是yes。 我們都知道 RocketMQ 和 Kafka 消息都是存在磁盤(pán)中的,那為什么消息存磁盤(pán)讀寫(xiě)還可以這么快?有沒(méi)有做了什么優(yōu)化?都是存磁盤(pán)它們兩者的實(shí)現(xiàn)之間有什么區(qū)別么?各自有什么優(yōu)缺點(diǎn)? 今天我們就來(lái)一探究竟。 存儲(chǔ)介質(zhì)-磁盤(pán) 一般而言消息中間件


大家好,我是yes。

我們都知道 RocketMQ 和 Kafka 消息都是存在磁盤(pán)中的,那為什么消息存磁盤(pán)讀寫(xiě)還可以這么快?有沒(méi)有做了什么優(yōu)化?都是存磁盤(pán)它們兩者的實(shí)現(xiàn)之間有什么區(qū)別么?各自有什么優(yōu)缺點(diǎn)?

今天我們就來(lái)一探究竟。

存儲(chǔ)介質(zhì)-磁盤(pán)

一般而言消息中間件的消息都存儲(chǔ)在本地文件中,因?yàn)閺男蕘?lái)看直接放本地文件是最快的,并且穩(wěn)定性最高。畢竟要是放類(lèi)似數(shù)據(jù)庫(kù)等第三方存儲(chǔ)中的話,就多一個(gè)依賴少一份安全,并且還有網(wǎng)絡(luò)的開(kāi)銷(xiāo)。

那對(duì)于將消息存入磁盤(pán)文件來(lái)說(shuō)一個(gè)流程的瓶頸就是磁盤(pán)的寫(xiě)入和讀取。我們知道磁盤(pán)相對(duì)而言讀寫(xiě)速度較慢,那通過(guò)磁盤(pán)作為存儲(chǔ)介質(zhì)如何實(shí)現(xiàn)高吞吐呢?

順序讀寫(xiě)

答案就是順序讀寫(xiě)。

首先了解一下頁(yè)緩存,頁(yè)緩存是操作系統(tǒng)用來(lái)作為磁盤(pán)的一種緩存,減少磁盤(pán)的I/O操作。

在寫(xiě)入磁盤(pán)的時(shí)候其實(shí)是寫(xiě)入頁(yè)緩存中,使得對(duì)磁盤(pán)的寫(xiě)入變成對(duì)內(nèi)存的寫(xiě)入。寫(xiě)入的頁(yè)變成臟頁(yè),然后操作系統(tǒng)會(huì)在合適的時(shí)候?qū)⑴K頁(yè)寫(xiě)入磁盤(pán)中。

在讀取的時(shí)候如果頁(yè)緩存命中則直接返回,如果頁(yè)緩存 miss 則產(chǎn)生缺頁(yè)中斷,從磁盤(pán)加載數(shù)據(jù)至頁(yè)緩存中,然后返回?cái)?shù)據(jù)。

并且在讀的時(shí)候會(huì)預(yù)讀,根據(jù)局部性原理當(dāng)讀取的時(shí)候會(huì)把相鄰的磁盤(pán)塊讀入頁(yè)緩存中。在寫(xiě)入的時(shí)候會(huì)后寫(xiě),寫(xiě)入的也是頁(yè)緩存,這樣存著可以將一些小的寫(xiě)入操作合并成大的寫(xiě)入,然后再刷盤(pán)。

而且根據(jù)磁盤(pán)的構(gòu)造,順序 I/O 的時(shí)候,磁頭幾乎不用換道,或者換道的時(shí)間很短。

根據(jù)網(wǎng)上的一些測(cè)試結(jié)果,順序?qū)懕P(pán)的速度比隨機(jī)寫(xiě)內(nèi)存還要快。

當(dāng)然這樣的寫(xiě)入存在數(shù)據(jù)丟失的風(fēng)險(xiǎn),例如機(jī)器突然斷電,那些還未刷盤(pán)的臟頁(yè)就丟失了。不過(guò)可以調(diào)用 fsync強(qiáng)制刷盤(pán),但是這樣對(duì)于性能的損耗較大。

因此一般建議通過(guò)多副本機(jī)制來(lái)保證消息的可靠,而不是同步刷盤(pán)

可以看到順序 I/O 適應(yīng)磁盤(pán)的構(gòu)造,并且還有預(yù)讀和后寫(xiě)。RocketMQ 和 Kafka 都是順序?qū)懭牒徒祈樞蜃x取。它們都采用文件追加的方式來(lái)寫(xiě)入消息,只能在日志文件尾部寫(xiě)入新的消息,老的消息無(wú)法更改。

mmap-文件內(nèi)存映射

從上面可知訪問(wèn)磁盤(pán)文件會(huì)將數(shù)據(jù)加載到頁(yè)緩存中,但是頁(yè)緩存屬于內(nèi)核空間,用戶空間訪問(wèn)不了,因此數(shù)據(jù)還需要拷貝到用戶空間緩沖區(qū)。

可以看到數(shù)據(jù)需要從頁(yè)緩存再經(jīng)過(guò)一次拷貝程序才能訪問(wèn)的到,因此還可以通過(guò)mmap來(lái)做一波優(yōu)化,利用內(nèi)存映射文件來(lái)避免拷貝。

簡(jiǎn)單的說(shuō)文件映射就是將程序虛擬頁(yè)面直接映射到頁(yè)緩存上,這樣就無(wú)需有內(nèi)核態(tài)再往用戶態(tài)的拷貝,而且也避免了重復(fù)數(shù)據(jù)的產(chǎn)生。并且也不必再通過(guò)調(diào)用read或write方法對(duì)文件進(jìn)行讀寫(xiě),可以通過(guò)映射地址加偏移量的方式直接操作。

sendfile-零拷貝

既然消息是存在磁盤(pán)中的,那消費(fèi)者來(lái)拉消息的時(shí)候就得從磁盤(pán)拿。我們先來(lái)看看一般發(fā)送文件的流程是如何的。

簡(jiǎn)單說(shuō)下DMA是什么,全稱(chēng) Direct Memory Access ,它可以獨(dú)立地直接讀寫(xiě)系統(tǒng)內(nèi)存,不需要 CPU 介入,像顯卡、網(wǎng)卡之類(lèi)都會(huì)用DMA。

可以看到數(shù)據(jù)其實(shí)是冗余的,那我們來(lái)看看mmap之后的發(fā)送文件流程是怎樣的。

可以看到上下文切換的次數(shù)沒(méi)有變化,但是數(shù)據(jù)少拷貝一份,這和我們上文提到的mmap能達(dá)到的效果是一樣的。

但是數(shù)據(jù)還是冗余了一份,這不是可以直接把數(shù)據(jù)從頁(yè)緩存拷貝到網(wǎng)卡不就好了嘛?sendfile就有這個(gè)功效。我們先來(lái)看看Linux2.1版本中的sendfile。

因?yàn)榫鸵粋€(gè)系統(tǒng)調(diào)用就滿足了發(fā)送的需求,相比 read + write或者 mmap + write上下文切換肯定是少了的,但是好像數(shù)據(jù)還是有冗余啊。是的,因此 Linux2.4 版本的 sendfile  + 帶 「分散-收集(Scatter-gather)」的DMA。實(shí)現(xiàn)了真正的無(wú)冗余。

這就是我們常說(shuō)的零拷貝,在 Java 中FileChannal.transferTo()底層用的就是sendfile。

接下來(lái)我們看看以上說(shuō)的幾點(diǎn)在 RocketMQ 和 Kafka中是如何應(yīng)用的。

RocketMQ 和 Kafka 的應(yīng)用

RocketMQ

采用Topic混合追加方式,即一個(gè) CommitLog 文件中會(huì)包含分給此 Broker 的所有消息,不論消息屬于哪個(gè) Topic 的哪個(gè) Queue 。

Kafka和RocketMQ底層存儲(chǔ)之那些你不知道的事

所以所有的消息過(guò)來(lái)都是順序追加寫(xiě)入到 CommitLog 中,并且建立消息對(duì)應(yīng)的 CosumerQueue ,然后消費(fèi)者是通過(guò) CosumerQueue 得到消息的真實(shí)物理地址再去 CommitLog 獲取消息的??梢詫?CosumerQueue 理解為消息的索引。

在 RocketMQ 中不論是 CommitLog 還是 CosumerQueue 都采用了 mmap。

在發(fā)消息的時(shí)候默認(rèn)用的是將數(shù)據(jù)拷貝到堆內(nèi)存中,然后再發(fā)送。我們來(lái)看下代碼。

Kafka和RocketMQ底層存儲(chǔ)之那些你不知道的事

可以看到這個(gè)配置 transferMsgByHeap默認(rèn)是 true ,那我們?cè)倏聪M(fèi)者拉消息時(shí)候的代碼。

可以看到 RocketMQ 默認(rèn)把消息拷貝到堆內(nèi) Buffer 中,再塞到響應(yīng)體里面發(fā)送。但是可以通過(guò)參數(shù)配置不經(jīng)過(guò)堆,不過(guò)也并沒(méi)有用到真正的零拷貝,而是通過(guò)mapedBuffer 發(fā)送到 SocketBuffer 。

所以 RocketMQ 用了順序?qū)懕P(pán)、mmap。并沒(méi)有用到 sendfile ,還有一步頁(yè)緩存到 SocketBuffer 的拷貝。

然后拉消息的時(shí)候嚴(yán)格的說(shuō)對(duì)于 CommitLog 來(lái)說(shuō)讀取是隨機(jī)的,因?yàn)?CommitLog 的消息是混合的存儲(chǔ)的,但是從整體上看,消息還是從 CommitLog 順序讀的,都是從舊數(shù)據(jù)到新數(shù)據(jù)有序的讀取。并且一般而言消息存進(jìn)去馬上就會(huì)被消費(fèi),因此消息這時(shí)候應(yīng)該還在頁(yè)緩存中,所以不需要讀盤(pán)。

而且我們?cè)谏厦嫣岬剑?strong>頁(yè)緩存會(huì)定時(shí)刷盤(pán),這刷盤(pán)不可控,并且內(nèi)存是有限的,會(huì)有swap等情況。

而且mmap其實(shí)只是做了映射,當(dāng)真正讀取頁(yè)面的時(shí)候產(chǎn)生缺頁(yè)中斷,才會(huì)將數(shù)據(jù)真正加載到內(nèi)存中,這對(duì)于消息隊(duì)列來(lái)說(shuō)可能會(huì)產(chǎn)生監(jiān)控上的毛刺。

因此 RocketMQ 做了一些優(yōu)化,有:文件預(yù)分配和文件預(yù)熱。

文件預(yù)分配

CommitLog 的大小默認(rèn)是1G,當(dāng)超過(guò)大小限制的時(shí)候需要準(zhǔn)備新的文件,而 RocketMQ 就起了一個(gè)后臺(tái)線程 AllocateMappedFileService,不斷的處理 AllocateRequest,AllocateRequest其實(shí)就是預(yù)分配的請(qǐng)求,會(huì)提前準(zhǔn)備好下一個(gè)文件的分配,防止在消息寫(xiě)入的過(guò)程中分配文件,產(chǎn)生抖動(dòng)。

文件預(yù)熱

有一個(gè)warmMappedFile方法,它會(huì)把當(dāng)前映射的文件,每一頁(yè)遍歷多去,寫(xiě)入一個(gè)0字節(jié),然后再調(diào)用mlock和 madvise(MADV_WILLNEED)。

我們?cè)賮?lái)看下this.mlock,內(nèi)部其實(shí)就是調(diào)用了mlock和 madvise(MADV_WILLNEED)。

mlock:可以將進(jìn)程使用的部分或者全部的地址空間鎖定在物理內(nèi)存中,防止其被交換到swap空間。

madvise:給操作系統(tǒng)建議,說(shuō)這文件在不久的將來(lái)要訪問(wèn)的,因此,提前讀幾頁(yè)可能是個(gè)好主意。

RocketMQ 小結(jié)

順序?qū)懕P(pán),整體來(lái)看是順序讀盤(pán),并且使用了 mmap,不是真正的零拷貝。又因?yàn)轫?yè)緩存的不確定性和 mmap 惰性加載(訪問(wèn)時(shí)缺頁(yè)中斷才會(huì)真正加載數(shù)據(jù)),用了文件預(yù)先分配和文件預(yù)熱即每頁(yè)寫(xiě)入一個(gè)0字節(jié),然后再調(diào)用mlock和 madvise(MADV_WILLNEED)。

Kafka

Kafka 的日志存儲(chǔ)和 RocketMQ 不一樣,它是一個(gè)分區(qū)一個(gè)文件。

Kafka 的消息寫(xiě)入對(duì)于單分區(qū)來(lái)說(shuō)也是順序?qū)?,如果分區(qū)不多的話從整體上看也算順序?qū)?,它的日志文件并沒(méi)有用到 mmap,而索引文件用了 mmap。但發(fā)消息 Kafka 用到了零拷貝。

對(duì)于消息的寫(xiě)入來(lái)說(shuō) mmap 其實(shí)沒(méi)什么用,因?yàn)橄⑹菑木W(wǎng)絡(luò)中來(lái)。而對(duì)于發(fā)消息來(lái)說(shuō) sendfile 對(duì)比 mmap+write 我覺(jué)得效率更高,因?yàn)樯倭艘淮雾?yè)緩存到 SocketBuffer 中的拷貝。

來(lái)看下Kafka發(fā)消息的源碼,最終調(diào)用的是 FileChannel.transferTo,底層就是 sendfile。

從 Kafka 源碼中我沒(méi)看到有類(lèi)似于 RocketMQ的 mlock 等操作,我覺(jué)得原因是首先日志也沒(méi)用到 mmap,然后 swap 其實(shí)可以通過(guò) Linux 系統(tǒng)參數(shù) vm.swappiness來(lái)調(diào)節(jié),這里建議設(shè)置為1,而不是0。

假設(shè)內(nèi)存真的不足,設(shè)置為 0 的話,在內(nèi)存耗盡的情況下,又不能 swap,則會(huì)突然中止某些進(jìn)程。設(shè)置個(gè) 1,起碼還能拖一下,如果有良好的監(jiān)控手段,還能給個(gè)機(jī)會(huì)發(fā)現(xiàn)一下,不至于突然中止。

RocketMQ & Kafka 對(duì)比

首先都是順序?qū)懭耄贿^(guò) RocketMQ 是把消息都存一個(gè)文件中,而 Kafka 是一個(gè)分區(qū)一個(gè)文件

每個(gè)分區(qū)一個(gè)文件在遷移或者數(shù)據(jù)復(fù)制層面上來(lái)說(shuō)更加得靈活。

但是分區(qū)多了的話,寫(xiě)入需要頻繁的在多個(gè)文件之間來(lái)回切換,對(duì)于每個(gè)文件來(lái)說(shuō)是順序?qū)懭氲?,但是從全局看其?shí)算隨機(jī)寫(xiě)入,并且讀取的時(shí)候也是一樣,算隨機(jī)讀。而就一個(gè)文件的 RocketMQ 就沒(méi)這個(gè)問(wèn)題。

從發(fā)送消息來(lái)說(shuō) RocketMQ 用到了 mmap + write 的方式,并且通過(guò)預(yù)熱來(lái)減少大文件 mmap 因?yàn)槿表?yè)中斷產(chǎn)生的性能問(wèn)題。而 Kafka 則用了 sendfile,相對(duì)而言我覺(jué)得 kafka 發(fā)送的效率更高,因?yàn)樯倭艘淮雾?yè)緩存到 SocketBuffer 中的拷貝。

并且 swap 問(wèn)題也可以通過(guò)系統(tǒng)參數(shù)來(lái)設(shè)置。


特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

Kafka和RocketMQ底層存儲(chǔ)之那些你不知道的事

Kafka和RocketMQ底層存儲(chǔ)之那些你不知道的事

Kafka和RocketMQ底層存儲(chǔ)之那些你不知道的事

長(zhǎng)按訂閱更多精彩▼

Kafka和RocketMQ底層存儲(chǔ)之那些你不知道的事

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

ckquote>
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉