【詳解文件IO系列】講講 MQ 消息中間件 (Kafka佑钾,RocketMQ等)與 MMAP西疤、PageCache 的故事

一般的IO調用

首先來看一下一般的IO調用。在傳統(tǒng)的文件IO操作中休溶,我們都是調用操作系統(tǒng)提供的底層標準IO系統(tǒng)調用函數(shù) read()代赁、write() ,此時調用此函數(shù)的進程(在JAVA中即java進程)由當前的用戶態(tài)切換到內核態(tài)兽掰,然后OS的內核代碼負責將相應的文件數(shù)據(jù)讀取到內核的IO緩沖區(qū)芭碍,然后再把數(shù)據(jù)從內核IO緩沖區(qū)拷貝到進程的私有地址空間中去,這樣便完成了一次IO操作孽尽。如下圖所示窖壕。

注意兩點:

  • OS的read函數(shù)會在內核IO緩沖區(qū)中預讀取數(shù)據(jù),減少磁盤IO操作(Step2)
  • Java的BufferedReader或BufferedInputStream的緩沖區(qū)的作用是減少系統(tǒng)調用(Step1)

Java的IO讀寫大致分為三種:

1、普通IO(java.io)

例如FileWriter瞻讽、FileReader等鸳吸,普通IO是傳統(tǒng)字節(jié)傳輸方式,讀寫慢阻塞速勇,單向一個Read對應一個Write 晌砾。

2、文件通道 FileChannel(java.nio)

FileChannel fileChannel = new RandomAccessFile(new File("data.txt"), "rw").getChannel()
  • 全雙工通道快集,采用內存緩沖區(qū)ByteBuffer且是線程安全的

  • 使用FileChannel為什么會比普通IO快贡羔?
    一般情況FileChannel在一次寫入4kb的整數(shù)倍數(shù)時,才能發(fā)揮出實際的性能个初,益于FileChannel采用了ByteBuffer這樣的內存緩沖區(qū)乖寒。這樣可以精準控制寫入磁盤的大小,這是普通IO無法實現(xiàn)

  • FileChannel是直接把ByteBuffer的數(shù)據(jù)直接寫入磁盤院溺?
    ByteBuffer 中的數(shù)據(jù)和磁盤中的數(shù)據(jù)還隔了一層楣嘁,這一層便是 PageCache,是用戶內存和磁盤之間的一層緩存珍逸。我們都知道磁盤 IO 和內存 IO 的速度可是相差了好幾個數(shù)量級逐虚。我們可以認為 filechannel.write 寫入 PageCache 便是完成了落盤操作,但實際上谆膳,操作系統(tǒng)最終幫我們完成了 PageCache 到磁盤的最終寫入叭爱,理解了這個概念,你就應該能夠理解 FileChannel 為什么提供了一個 force() 方法漱病,用于通知操作系統(tǒng)進行及時的刷盤买雾,同理使用FileChannel時同樣經歷磁盤->PageCache->用戶內存三個階段

3、內存映射MMAP(java.nio)

MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, position, fileSize)

mmap 把文件映射到用戶空間里的虛擬內存杨帽,省去了從內核緩沖區(qū)復制到用戶空間的過程漓穿,文件中的位置在虛擬內存中有了對應的地址,可以像操作內存一樣操作這個文件注盈,相當于已經把整個文件放入內存晃危,但在真正使用到這些數(shù)據(jù)前卻不會消耗物理內存,也不會有讀寫磁盤的操作老客,只有真正使用這些數(shù)據(jù)時僚饭,也就是圖像準備渲染在屏幕上時,虛擬內存管理系統(tǒng) VMS

MMAP 并非是文件 IO 的銀彈胧砰,它只有在一次寫入很小量數(shù)據(jù)的場景下才能表現(xiàn)出比 FileChannel 稍微優(yōu)異的性能浪慌。緊接著我還要告訴你一些令你沮喪的事,至少在 JAVA 中使用 MappedByteBuffer 是一件非常麻煩并且痛苦的事朴则,主要表現(xiàn)為三點:

  • MMAP 使用時必須實現(xiàn)指定好內存映射的大小,并且一次 map 的大小限制在 1.5G 左右,重復 map 又會帶來虛擬內存的回收乌妒、重新分配的問題汹想,對于文件不確定大小的情形實在是太不友好了。

  • MMAP 使用的是虛擬內存撤蚊,和 PageCache 一樣是由操作系統(tǒng)來控制刷盤的古掏,雖然可以通過 force() 來手動控制,但這個時間把握不好侦啸,在小內存場景下會很令人頭疼槽唾。

  • MMAP 的回收問題,當 MappedByteBuffer 不再需要時光涂,可以手動釋放占用的虛擬內存庞萍,但…方式非常的詭異

OS 的 PageCache機制

PageCache是OS對文件的緩存,用于加速對文件的讀寫忘闻。一般來說钝计,程序對文件進行順序讀寫的速度幾乎接近于內存的讀寫訪問,這里的主要原因就是在于OS使用PageCache機制對讀寫訪問操作進行了性能優(yōu)化齐佳,將一部分的內存用作PageCache
1私恬、對于數(shù)據(jù)文件的讀取

如果一次讀取文件時出現(xiàn)未命中(cache miss)PageCache的情況,OS從物理磁盤上訪問讀取文件的同時炼吴,會順序對其他相鄰塊的數(shù)據(jù)文件進行預讀缺久(ps:順序讀入緊隨其后的少數(shù)幾個頁面)。這樣硅蹦,只要下次訪問的文件已經被加載至PageCache時荣德,讀取操作的速度基本等于訪問內存
1、對于數(shù)據(jù)文件的寫入

OS會先寫入至Cache內提针,隨后通過異步的方式由pdflush內核線程將Cache內的數(shù)據(jù)刷盤至物理磁盤上
對于文件的順序讀寫操作來說命爬,讀和寫的區(qū)域都在OS的PageCache內,此時讀寫性能接近于內存辐脖。RocketMQ的大致做法是饲宛,將數(shù)據(jù)文件映射到OS的虛擬內存中(通過JDK NIO的MappedByteBuffer),寫消息的時候首先寫入PageCache嗜价,并通過異步刷盤的方式將消息批量的做持久化(同時也支持同步刷盤)艇抠;訂閱消費消息時(對CommitLog操作是隨機讀取)久锥,由于PageCache的局部性熱點原理且整體情況下還是從舊到新的有序讀家淤,因此大部分情況下消息還是可以直接從Page Cache(cache hit)中讀取,不會產生太多的缺頁(Page Fault)中斷而從磁盤讀取:

PageCache機制也不是完全無缺點的瑟由,當遇到OS進行臟頁回寫絮重,內存回收,內存swap等情況時,就會引起較大的消息讀寫延遲青伤。

對于這些情況督怜,RocketMQ采用了多種優(yōu)化技術,比如內存預分配狠角,文件預熱号杠,mlock系統(tǒng)調用等,來保證在最大可能地發(fā)揮PageCache機制優(yōu)點的同時丰歌,盡可能地減少其缺點帶來的消息讀寫延遲

RocketMQ存儲優(yōu)化技術

對于RocketMQ來說姨蟋,它是把內存映射文件串聯(lián)起來,組成了鏈表立帖;因為內存映射文件本身大小有限制眼溶,只能是2G(默認1G);所以需要把多個內存映射文件串聯(lián)成一個鏈表厘惦;這里介紹RocketMQ存儲層采用的幾項優(yōu)化技術方案在一定程度上可以減少PageCache的缺點帶來的影響偷仿,主要包括內存預分配,文件預熱和mlock系統(tǒng)調用

1宵蕉、預分配MappedFile

在消息寫入過程中(調用CommitLog的putMessage()方法)愿险,CommitLog會先從MappedFileQueue隊列中獲取一個 MappedFile宋税,如果沒有就新建一個谷醉;這里一睁,MappedFile的創(chuàng)建過程是將構建好的一個AllocateRequest請求(具體做法是,將下一個文件的路徑稼稿、下下個文件的路徑薄榛、文件大小為參數(shù)封裝為AllocateRequest對象)添加至隊列中,后臺運行的AllocateMappedFileService服務線程(在Broker啟動時让歼,該線程就會創(chuàng)建并運行)敞恋,會不停地run,只要請求隊列里存在請求谋右,就會去執(zhí)行MappedFile映射文件的創(chuàng)建和預分配工作硬猫,分配的時候有兩種策略,一種是使用Mmap的方式來構建MappedFile實例改执,另外一種是從TransientStorePool堆外內存池中獲取相應的DirectByteBuffer來構建MappedFile(ps:具體采用哪種策略啸蜜,也與刷盤的方式有關)。并且辈挂,在創(chuàng)建分配完下個MappedFile后衬横,還會將下下個MappedFile預先創(chuàng)建并保存至請求隊列中等待下次獲取時直接返回。RocketMQ中預分配MappedFile的設計非常巧妙终蒂,下次獲取時候直接返回就可以不用等待MappedFile創(chuàng)建分配所產生的時間延遲

2 文件預熱 && mlock系統(tǒng)調用(TransientStorePool)

mlock系統(tǒng)調用

其可以將進程使用的部分或者全部的地址空間鎖定在物理內存中蜂林,防止其被交換到swap空間遥诉。對于RocketMQ這種的高吞吐量的分布式消息隊列來說,追求的是消息讀寫低延遲噪叙,那么肯定希望盡可能地多使用物理內存突那,提高數(shù)據(jù)讀寫訪問的操作效率。

文件預熱

預熱的目的主要有兩點:

第一點构眯,由于僅分配內存并進行mlock系統(tǒng)調用后并不會為程序完全鎖定這些內存,因為其中的分頁可能是寫時復制的早龟。因此惫霸,就有必要對每個內存頁面中寫入一個假的值。其中葱弟,RocketMQ是在創(chuàng)建并分配MappedFile的過程中壹店,預先寫入一些隨機值至Mmap映射出的內存空間里。

第二芝加,調用Mmap進行內存映射后硅卢,OS只是建立虛擬內存地址至物理地址的映射表,而實際并沒有加載任何文件至內存中藏杖。程序要訪問數(shù)據(jù)時OS會檢查該部分的分頁是否已經在內存中将塑,如果不在,則發(fā)出一次缺頁中斷蝌麸。這里点寥,可以想象下1G的CommitLog需要發(fā)生多少次缺頁中斷,才能使得對應的數(shù)據(jù)才能完全加載至物理內存中(ps:X86的Linux中一個標準頁面大小是4KB)来吩?

RocketMQ的做法是

在做Mmap內存映射的同時進行madvise系統(tǒng)調用敢辩,目的是使OS做一次內存映射后對應的文件數(shù)據(jù)盡可能多的預加載至內存中,從而達到內存預熱的效果弟疆。

參考資料

https://my.oschina.net/u/3180962/blog/3064148
https://blog.csdn.net/linxdcn/article/details/72903422
http://www.reibang.com/p/6d0c118c17de

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末戚长,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子怠苔,更是在濱河造成了極大的恐慌同廉,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘀略,死亡現(xiàn)場離奇詭異恤溶,居然都是意外死亡,警方通過查閱死者的電腦和手機帜羊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門咒程,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人讼育,你說我怎么就攤上這事帐姻〕砑” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵饥瓷,是天一觀的道長剥纷。 經常有香客問我,道長呢铆,這世上最難降的妖魔是什么晦鞋? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮棺克,結果婚禮上悠垛,老公的妹妹穿的比我還像新娘。我一直安慰自己娜谊,他們只是感情好确买,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纱皆,像睡著了一般湾趾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上派草,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天搀缠,我揣著相機與錄音,去河邊找鬼澳眷。 笑死胡嘿,一個胖子當著我的面吹牛,可吹牛的內容都是我干的钳踊。 我是一名探鬼主播衷敌,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拓瞪!你這毒婦竟也來了缴罗?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤祭埂,失蹤者是張志新(化名)和其女友劉穎面氓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蛆橡,經...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡舌界,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泰演。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呻拌。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖睦焕,靈堂內的尸體忽然破棺而出藐握,到底是詐尸還是另有隱情靴拱,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布猾普,位于F島的核電站袜炕,受9級特大地震影響,放射性物質發(fā)生泄漏初家。R本人自食惡果不足惜偎窘,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溜在。 院中可真熱鬧评架,春花似錦、人聲如沸炕泳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽培遵。三九已至,卻和暖如春登刺,著一層夾襖步出監(jiān)牢的瞬間籽腕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工纸俭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留皇耗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓揍很,卻偏偏與公主長得像郎楼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子窒悔,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355