<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      消息隊(duì)列的存儲(chǔ)設(shè)計(jì),就看這一篇

      基礎(chǔ)知識

      一、零拷貝

      目的:

      1. 減少或避免不必要的CPU拷貝, 2. 減少用戶空間(應(yīng)用程序自己的空間)和內(nèi)核空間(linux內(nèi)核自身的空間,包括進(jìn)程調(diào)度、連接硬件資源、內(nèi)存分配等)的上下文切換, 3. 減少內(nèi)存的占用

      典型應(yīng)用:

      Netty、Kafka等

      基本概念:

      1.  緩沖區(qū):是所有I/O的基礎(chǔ),I/O 無非就是把數(shù)據(jù)移進(jìn)或移出緩沖區(qū)。

      2. 虛擬內(nèi)存:通過虛擬技術(shù),將外部存儲(chǔ)設(shè)備的一部分空間,劃分給系統(tǒng),作為在內(nèi)存不足時(shí)臨時(shí)用作數(shù)據(jù)緩存。

      3. 直接內(nèi)存訪問(Direct Memory Access)(DMA):DMA允許不同速度的硬件裝置來溝通,而不需要依于 CPU 的大量中斷負(fù)載,是一種可以大大減輕 CPU 工作量的數(shù)據(jù)轉(zhuǎn)移方式。基于 DMA 訪問方式,系統(tǒng)主內(nèi)存于硬盤或網(wǎng)卡之間的數(shù)據(jù)傳輸可以繞開 CPU 的調(diào)度。

      額外提一下,RDMA(Remote Direct Memory Access):遠(yuǎn)程直接內(nèi)存訪問,允許遠(yuǎn)程訪問磁盤IO,減少CPU參與。

           ------》        

       

       把磁盤控制器緩存區(qū)和內(nèi)核緩沖區(qū)之間的數(shù)據(jù)拷貝,由CPU轉(zhuǎn)移到DMA去做。通過 DMA 和虛擬內(nèi)存技術(shù),我們實(shí)現(xiàn)了 Zero Copy 的目標(biāo),IO 設(shè)備跟用戶程序空間傳輸數(shù)據(jù)的過程中,減少數(shù)據(jù)拷貝次數(shù),減少系統(tǒng)調(diào)用,實(shí)現(xiàn) CPU 的零參與,徹底消除 CPU 在這方面的負(fù)載。(圖片來源網(wǎng)絡(luò),鏈接建參考)

       

      見下圖:把內(nèi)核空間地址和用戶空間的虛擬地址映射到同一個(gè)物理地址(下圖物理內(nèi)存藍(lán)色區(qū)塊),這樣DMA就可以填充(讀寫)對內(nèi)核和用戶空間進(jìn)程同時(shí)可見的緩沖區(qū)了。

      (來源網(wǎng)絡(luò))

       

       傳統(tǒng)IO

       

      (說明:應(yīng)用進(jìn)程的堆內(nèi)和堆外內(nèi)存都在用戶空間)

      4次用戶態(tài)到內(nèi)核態(tài)上下文切換、4次拷貝(2次CPU復(fù)制)。

       零拷貝可以通過FileChannel的sendfile或者mmap+write方式實(shí)現(xiàn),減少用戶空間和內(nèi)核空間之間的內(nèi)存拷貝/CPU復(fù)制/上下文切換。

      mmap+write實(shí)現(xiàn)的零拷貝

       

      用戶空間和內(nèi)核空間映射

      4次用戶態(tài)到內(nèi)核態(tài)的上下文切換、3次拷貝(其中一次CPU復(fù)制)。

      sendfile實(shí)現(xiàn)的零拷貝 

      (有文檔介紹說linux 內(nèi)核2.6以后版本不支持sendfile)

       

      2次上下文切換、3次拷貝(其中1次CPU復(fù)制)。

      對比:

      mmap+write方式多了2次mmap/write用戶態(tài)到內(nèi)核態(tài)的上下文切換,他們比傳統(tǒng)IO(fileInputstream.write/read方式)少1次CPU復(fù)制(都用DMA復(fù)制的情況下)。

      DMA復(fù)制,減少了2次磁盤和內(nèi)核數(shù)據(jù)傳輸導(dǎo)致的CPU占用。

       

      二、Page Cache

      Page cache是通過將磁盤中的數(shù)據(jù)緩存到內(nèi)存中,為了減少磁盤I/O操作,提高性能。

      由物理page組成,內(nèi)容對應(yīng)磁盤的block。大小是動(dòng)態(tài)變化的,可以擴(kuò)大,也可以在內(nèi)存不足時(shí)縮小。一個(gè)page通常包括多個(gè)block。

      page cache可以大大加快文件的讀寫速度,一次讀取或?qū)懭?strong>4k的數(shù)據(jù),節(jié)省連接的各種開銷。程序?qū)?shù)據(jù)先寫入page cache,在fsync到磁盤(page cache回寫)中。

      但是,一旦斷電或者是故障,數(shù)據(jù)會(huì)丟失,沒辦法保障數(shù)據(jù)安全。

      page cache會(huì)根據(jù)策略刷入磁盤,比如,2G內(nèi)存,規(guī)定50%刷入磁盤,超過1G后就會(huì)將page cache刷入磁盤。如果不足1G,這部分內(nèi)存稱為臟頁,如果忽然斷電,臟頁會(huì)丟失的。如果直接來了一個(gè)超過2G的數(shù)據(jù)寫入了page cache,會(huì)通過LRU算法(最近最常用的數(shù)據(jù)保留),通過swap交換硬盤的空間,將最不常用的數(shù)據(jù)刷入swap區(qū)中。臟頁會(huì)先刷到磁盤,才可以淘汰,保證數(shù)據(jù)不丟。但是不是臟頁,會(huì)直接通過LRU淘汰調(diào)最遠(yuǎn)最不常用數(shù)據(jù)。

      在Linux內(nèi)核中,文件的每個(gè)數(shù)據(jù)塊最多只能對應(yīng)一個(gè)page cache項(xiàng),它通過兩個(gè)數(shù)據(jù)結(jié)構(gòu)來管理這些cache項(xiàng),一個(gè)是radix tree,另一個(gè)是雙向鏈表。
      Radix tree是一種搜索樹,Linux內(nèi)核利用這個(gè)數(shù)據(jù)結(jié)構(gòu),快速查找臟的(dirty)和回寫的(writeback)頁面,得到其文件內(nèi)偏移,從而對page cache進(jìn)行快速定位。圖1是radix tree的一個(gè)示意圖,該radix tree的分叉為4(22),樹高為4,用來快速定位8位文件內(nèi)偏移。
      另一個(gè)數(shù)據(jù)結(jié)構(gòu)是雙向鏈表,Linux內(nèi)核為每一片物理內(nèi)存區(qū)域(zone)維護(hù)active_list和 inactive_list兩個(gè)雙向鏈表,這兩個(gè)list主要用來實(shí)現(xiàn)物理內(nèi)存的回收。這兩個(gè)鏈表上除了文件Cache之外,還包括其它匿名 (Anonymous)內(nèi)存,如進(jìn)程堆棧等。

      Page Cache 操作API

      FileChannel:

       

       

      讀寫主要兩種,1. FileChannel的read/write/sendfile(linux內(nèi)核有些版本不支持sendfile)

       

                          2. mmap

       mmap原理

       

       傳統(tǒng)文件I/O和內(nèi)存文件映射的過程圖的區(qū)別。內(nèi)存文件映射是將文件直接映射至用戶空間內(nèi)存,未經(jīng)過內(nèi)核空間緩沖區(qū)的拷貝,相對于傳統(tǒng)的I/O減少一次內(nèi)存拷貝。

      transferFrom和transferTo原理

       

       Page Cache回寫

      page cache回寫是指 將page cache寫入磁盤中。回寫后,系統(tǒng)也會(huì)將page cache這部分內(nèi)存回收。

      觸發(fā)條件

      1. 空間層面: 臟數(shù)據(jù)占比閾值(dirty_background_ratio)+ 臟數(shù)據(jù)大小閾值(dirty_background_bytes,優(yōu)先級別高于前者)。

      分級別:

      1)略超閾值,比如臟數(shù)據(jù)占比默認(rèn)為10%,超過10%,不足20%,后端異步線程回寫;

      2)嚴(yán)重超閾值,比如臟數(shù)據(jù)占比超過20%,堵塞page cache write程序,進(jìn)行回寫。

      2. 從時(shí)間的層面:即周期性的回寫(dirty_writeback_interval)

      3. 用戶主動(dòng)發(fā)起sync()/msync()/fsync()調(diào)用

      線程

       

      三、mmap、allocateDirect+write和allocate+write性能對比

      Java中NIO的核心緩沖就是ByteBuffer,所有的IO操作都是通過這個(gè)ByteBuffer進(jìn)行的;

      Bytebuffer有兩種: 分配HeapByteBufferByteBuffer buffer = ByteBuffer.allocate(int capacity);

      分配DirectByteBufferByteBuffer buffer = ByteBuffer.allocateDirect(int capacity);

       

      HeapByteBuffer會(huì)多一次拷貝:

      傳統(tǒng) BIO 是面向 Stream 的,底層實(shí)現(xiàn)可以理解為寫入的是 byte 數(shù)組,調(diào)用 native 方法寫入 IO,傳的參數(shù)是這個(gè)數(shù)組,就算GC改變了內(nèi)存地址,但是拿這個(gè)數(shù)組的引用照樣能找到最新的地址,對應(yīng)的方法時(shí)是:FileOutputStream.write

       但是NIO,為了提升效率,傳的是內(nèi)存地址,省去了一次間接應(yīng)用。GC會(huì)回收無用對象,同時(shí)還會(huì)進(jìn)行碎片整理,移動(dòng)對象在內(nèi)存中的位置,來減少內(nèi)存碎片。如果在調(diào)用系統(tǒng)調(diào)用時(shí),發(fā)生了GC,導(dǎo)致HeapByteBuffer內(nèi)存位置發(fā)生了變化,但是內(nèi)核態(tài)并不能感知到這個(gè)變化導(dǎo)致系統(tǒng)調(diào)用讀取或者寫入錯(cuò)誤的數(shù)據(jù)。而DirectByteBuffer不受GC控制。所以HeapByteBuffer會(huì)多一次拷貝到堆外內(nèi)存的過程。(題外話 mmap用到的MappedByteBuffer也是堆外)

      1. Direct buffer(allocateDirect)是相當(dāng)于固定的內(nèi)核buffer還是JVM進(jìn)程內(nèi)的堆外內(nèi)存?J

      VM進(jìn)程的堆外內(nèi)存,屬于用戶空間。

      2. Direct buffer的好處和壞處

      好處:

      a. 相比HeapByteBuffer,少一次堆內(nèi)拷貝到堆外的過程

      b. gc壓力小

      壞處:

      自己管理內(nèi)存。創(chuàng)建開銷大。

       

      對比代碼:

       

      import org.junit.Test;

      import java.io.File;
      import java.io.RandomAccessFile;
      import java.nio.ByteBuffer;
      import java.nio.MappedByteBuffer;
      import java.nio.channels.FileChannel;

      public class Demo {

      private final int writeSize = 1024 * 1024; // 單次寫入大小
      private final int totalSize = 1024 * 1024 * 1024; // 總的大小

      //allocate+write

      @Test
      public void testWriteFile() throws Exception {
      final String fileName = "/Users/chengzhiliang/temp/test.dat";
      File file = new File(fileName);
      RandomAccessFile raf = new RandomAccessFile(file, "rw");
      FileChannel fileChannel = raf.getChannel();
      ByteBuffer buffer = ByteBuffer.allocate(writeSize);
      byte b = 110;
      for (int i = 0; i < writeSize; i++) {
      buffer.put(b);
      }

      //write byte buffer
      long start = SystemClock.now();

      for (int i = 0; i < totalSize / writeSize; i++) {
      buffer.flip();
      fileChannel.write(buffer);
      }

      System.out.println("Write byte buffer to channel elapse " + (SystemClock.now() - start) + " ms");

      fileChannel.close();
      }

      //mmap

      @Test
      public void testWriteFile1() throws Exception {
      final String fileName = "/Users/chengzhiliang/temp/test.dat";
      File file = new File(fileName);
      RandomAccessFile raf = new RandomAccessFile(file, "rw");
      FileChannel fileChannel = raf.getChannel();
      ByteBuffer buffer = ByteBuffer.allocate(writeSize);
      byte b = 110;
      for (int i = 0; i < writeSize; i++) {
      buffer.put(b);
      }

      //write mapped byte buffer

      //mappedByteBuffer屬于堆外內(nèi)存

      long start = SystemClock.now();
      MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, totalSize);
      for (int i = 0; i < totalSize / writeSize; i++) {
      buffer.flip();
      mappedByteBuffer.put(buffer);
      }

      System.out.println("Write mapped byte buffer elapse " + (SystemClock.now() - start) + " ms");

      fileChannel.close();
      }

      //allocateDirect+write

      @Test
      public void testWriteFile2() throws Exception {
      final String fileName = "/Users/chengzhiliang/temp/test.dat";
      File file = new File(fileName);
      RandomAccessFile raf = new RandomAccessFile(file, "rw");
      FileChannel fileChannel = raf.getChannel();

      byte b = 110;
      ByteBuffer directBuffer = ByteBuffer.allocateDirect(writeSize);
      for (int i = 0; i < writeSize; i++) {
      directBuffer.put(b);
      }

      //write direct buffer
      long start = SystemClock.now();

      for (int i = 0; i < totalSize / writeSize; i++) {
      directBuffer.flip();
      fileChannel.write(directBuffer);
      }

      System.out.println("Write direct byte buffer to channel elapse " + (SystemClock.now() - start) + " ms");

      fileChannel.close();
      }
      }

       

      對比結(jié)果,總共寫入1GB文件,采用上述三次方式,每次分別寫入1KB、10KB、100KB、1MB,發(fā)現(xiàn)100KB是個(gè)分水嶺,1KB和10KB 性能mmap > allocateDirect(堆外) > allocate(堆內(nèi));100KB以后, allocateDirect > mmap.

      自研MQ采用allocate+write寫,mmap讀的方式。

       

      四、各MQ的存儲(chǔ)結(jié)構(gòu)對比

      待完善。。。。

       

       

       

       

       

      參考:

      FileChannel詳解:http://www.rzrgm.cn/lxyit/p/9170741.html

      框架篇:Linux零拷貝機(jī)制和FileChannel   http://www.rzrgm.cn/cscw/p/13883420.html

      DMA 技術(shù)是什么,在哪里用?看完絕對有收獲  https://www.jianshu.com/p/3a26e8c9f402

       

      Java 堆外內(nèi)存、零拷貝、直接內(nèi)存以及針對于NIO中的FileChannel的思考 https://zhuanlan.zhihu.com/p/161939673

       

       

       

       

       

       

      posted on 2022-03-30 18:05  穎月石  閱讀(261)  評論(0)    收藏  舉報(bào)

      主站蜘蛛池模板: 久久热这里这里只有精品| 久草热久草热线频97精品| 固镇县| √天堂中文www官网在线| 国产精品久久欧美久久一区| 又湿又紧又大又爽a视频| 欧美人成在线播放网站免费| 四虎永久精品在线视频| 融水| 人妻少妇精品视频二区 | 天水市| 大尺度国产一区二区视频| 欧美zoozzooz性欧美| 开心久久综合激情五月天 | 深夜福利啪啪片| 中文字幕有码在线第十页| 双腿张开被5个男人调教电影| 国产精品va无码一区二区| 久久国产免费观看精品3| 国产国拍亚洲精品永久软件| 精品国产美女av久久久久| 国产免费高清69式视频在线观看 | 亚洲最大av一区二区| 亚洲综合色成在线播放| 国产91精品调教在线播放| 成人午夜电影福利免费| 男女无遮挡激情视频| 久久天天躁狠狠躁夜夜躁2020 | 韩国精品一区二区三区在线观看| 亚洲成av人片天堂网无码 | 在线免费观看亚洲天堂av| 日韩狼人精品在线观看| 国产av中文字幕精品| 亚洲国产成人综合精品| 国产影片AV级毛片特别刺激| 泰和县| 国产精品一二三区蜜臀av| 国产在线观看免费观看| 亚洲岛国成人免费av| 欧美精品国产综合久久| 欧美级特黄aaaaaa片|