天天看點

網易視訊雲:探求BlockCache實作機制

網易視訊雲是網易傾力打造的一款基于雲計算的分布式多媒體處理叢集和專業音視訊技術,為客戶提供穩定流暢、低延遲時間、高并發的視訊直播、錄制、存儲、轉碼及點播等音視訊的PaaS服務。線上教育、遠端醫療、娛樂秀場、線上金融等各行業及企業使用者隻需經過簡單的開發即可打造在線上音視訊平台。

  LRUBlockCache

  LRUBlockCache是HBase目前預設的BlockCache機制,實作機制比較簡單。它使用一個ConcurrentHashMap管理BlockKey到Block的映射關系,緩存Block隻需要将BlockKey和對應的Block放入該HashMap中,查詢緩存就根據BlockKey從HashMap中擷取即可。同時該方案采用嚴格的LRU淘汰算法,當Block Cache總量達到一定門檻值之後就會啟動淘汰機制,最近最少使用的Block會被置換出來。在具體的實作細節方面,需要關注三點:

  1. 緩存分層政策

  HBase在LRU緩存基礎上,采用了緩存分層設計,将整個BlockCache分為三個部分:single-access、mutil-access和inMemory。需要特别注意的是,HBase系統中繼資料存放在InMemory區,是以設定資料屬性InMemory = true需要非常謹慎,確定此列族資料量很小且通路頻繁,否則有可能會将hbase.meta中繼資料擠出記憶體,嚴重影響所有業務性能。

  2. LRU淘汰算法實作

  系統在每次cache block時将BlockKey和Block放入HashMap後都會檢查BlockCache總量是否達到門檻值,如果達到門檻值,就會喚醒淘汰線程對Map中的Block進行淘汰。系統設定三個MinMaxPriorityQueue隊列,分别對應上述三個分層,每個隊列中的元素按照最近最少被使用排列,系統會優先poll出最近最少使用的元素,将其對應的記憶體釋放。可見,三個分層中的Block會分别執行LRU淘汰算法進行淘汰。

  3. LRU方案優缺點

  LRU方案使用JVM提供的HashMap管理緩存,簡單有效。但随着資料從single-access區晉升到mutil-access區,基本就伴随着對應的記憶體對象從young區到old區,晉升到old區的Block被淘汰後會變為記憶體垃圾,最終由CMS回收掉(Conccurent Mark Sweep,一種标記清除算法),然而這種算法會帶來大量的記憶體碎片,碎片空間一直累計就會産生臭名昭著的Full GC。尤其在大記憶體條件下,一次Full GC很可能會持續較長時間,甚至達到分鐘級别。大家知道Full GC是會将整個程序暫停的(稱為stop-the-wold暫停),是以長時間Full GC必然會極大影響業務的正常讀寫請求。也正因為這樣的弊端,SlabCache方案和BucketCache方案才會橫空出世。

  BucketCache

  相比LRUBlockCache,BucketCache實作相對比較複雜。它沒有使用JVM 記憶體管理算法來管理緩存,而是自己對記憶體進行管理,是以不會因為出現大量碎片導緻Full GC的情況發生。本節主要介紹BucketCache的具體實作方式(包括BucketCache的記憶體組織形式、緩存寫入讀取流程等)以及如何配置使用BucketCache。

  記憶體組織形式

  下圖是BucketCache的記憶體組織形式圖,其中上面部分是邏輯組織結構,下面部分是對應的實體組織結構。HBase啟動之後會在記憶體中申請大量的bucket,如下圖中黃色矩形所示,每個bucket的大小預設都為2MB。每個bucket會有一個baseoffset變量和一個size标簽,其中baseoffset變量表示這個bucket在實際實體空間中的起始位址,是以block的實體位址就可以通過baseoffset和該block在bucket的偏移量唯一确定;而size标簽表示這個bucket可以存放的block塊的大小,比如圖中左側bucket的size标簽為65KB,表示可以存放64KB的block,右側bucket的size标簽為129KB,表示可以存放128KB的block。

網易視訊雲:探求BlockCache實作機制

  HBase中使用BucketAllocator類實作對Bucket的組織管理:

  1. HBase會根據每個bucket的size标簽對bucket進行分類,相同size标簽的bucket由同一個BucketSizeInfo管理,如上圖,左側存放64KB block的bucket由65KB BucketSizeInfo管理,右側存放128KB block的bucket由129KB BucketSizeInfo管理。

  2. HBase在啟動的時候就決定了size标簽的分類,預設标簽有(4+1)K、(8+1)K、(16+1)K … (48+1)K、(56+1)K、(64+1)K、(96+1)K … (512+1)K。而且系統會首先從小到大周遊一次所有size标簽,為每種size标簽配置設定一個bucket,最後所有剩餘的bucket都配置設定最大的size标簽,預設配置設定(512+1)K,如下圖所示:

網易視訊雲:探求BlockCache實作機制

  3. Bucket的size标簽可以動态調整,比如64K的block數目比較多,65K的bucket被用完了以後,其他size标簽的完全空閑的bucket可以轉換成為65K的bucket,但是至少保留一個該size的bucket。

  Block緩存寫入、讀取流程

  下圖是block寫入緩存以及從緩存中讀取block的流程示意圖,圖中主要包括5個子產品,其中RAMCache是一個存儲blockkey和block對應關系的HashMap;WriteThead是整個block寫入的中心樞紐,主要負責異步的寫入block到記憶體空間;BucketAllocator在上一節詳細介紹過,主要實作對bucket的組織管理,為block配置設定記憶體空間;IOEngine是具體的記憶體管理子產品,主要實作将block資料寫入對應位址的記憶體空間;BackingMap也是一個HashMap,用來存儲blockKey與對應實體記憶體偏移量的映射關系,用來根據blockkey定位具體的block;其中紫線表示cache block流程,綠線表示get block流程。

網易視訊雲:探求BlockCache實作機制

  Block緩存寫入流程

  1. 将block寫入RAMCache。實際實作中,HBase設定了多個RAMCache,系統首先會根據blockkey進行hash,根據hash結果将block配置設定到對應的RAMCache中;

  2. WriteThead從RAMCache中取出所有的block。和RAMCache相同,HBase會同時啟動多個WriteThead并發的執行異步寫入,每個WriteThead對應一個RAMCache;

  3. 每個WriteThead會将周遊RAMCache中所有block資料,分别調用bucketAllocator為這些block配置設定記憶體空間;

  4. BucketAllocator會選擇與block大小對應的bucket進行存放(具體細節可以參考上節‘記憶體組織形式’所述),并且傳回對應的實體位址偏移量offset;

  5. WriteThead将block以及配置設定好的實體位址偏移量傳給IOEngine子產品,執行具體的記憶體寫入操作;

  6. 寫入成功後,将類似<blockkey,offset>這樣的映射關系寫入BackingMap中,友善後續查找時根據blockkey可以直接定位;

  Block緩存讀取流程

  1. 首先從RAMCache中查找。對于還沒有來得及寫入到bucket的緩存block,一定存儲在RAMCache中;

  2. 如果在RAMCache中沒有找到,再在BackingMap中根據blockKey找到對應實體偏移位址offset;

  3. 根據實體偏移位址offset可以直接從記憶體中查找對應的block資料;

  BucketCache工作模式

  BucketCache預設有三種工作模式:heap、offheap和file;這三種工作模式在記憶體邏輯組織形式以及緩存流程上都是相同的,參見上節講解。不同的是三者對應的最終存儲媒體有所不同,即上述所講的IOEngine有所不同。

  其中heap模式和offheap模式都使用記憶體作為最終存儲媒體,記憶體配置設定查詢也都使用Java NIO ByteBuffer技術,不同的是,heap模式配置設定記憶體會調用byteBuffer.allocate方法,從JVM提供的heap區配置設定,而後者會調用byteBuffer.allocateDirect方法,直接從作業系統配置設定。這兩種記憶體配置設定模式會對HBase實際工作性能産生一定的影響。影響最大的無疑是GC ,相比heap模式,offheap模式因為記憶體屬于作業系統,是以基本不會産生CMS GC,也就在任何情況下都不會因為記憶體碎片導緻觸發Full GC。除此之外,在記憶體配置設定以及讀取方面,兩者性能也有不同,比如,記憶體配置設定時heap模式需要首先從作業系統配置設定記憶體再拷貝到JVM heap,相比offheap直接從作業系統配置設定記憶體更耗時;但是反過來,讀取緩存時heap模式可以從JVM heap中直接讀取,而offheap模式則需要首先從作業系統拷貝到JVM heap再讀取,顯得後者更費時。

  file模式和前面兩者不同,它使用Fussion-IO或者SSD等作為存儲媒體,相比昂貴的記憶體,這樣可以提供更大的存儲容量,是以可以極大地提升緩存命中率。

  BucketCache配置使用

  BucketCache方案的配置說明一直被HBaser所诟病,官方一直沒有相關文檔對此進行介紹。本人也是一直被其所困,後來通過檢視源碼才基本了解清楚,在此分享出來,以便大家學習。需要注意的是,BucketCache三種工作模式的配置會有所不同,下面也是分開介紹,并且沒有列出很多不重要的參數:

  heap模式

  <hbase.bucketcache.ioengine>heap</hbase.bucketcache.ioengine>

  //bucketcache占用整個jvm記憶體大小的比例

  <hbase.bucketcache.size>0.4</hbase.bucketcache.size>

  //bucketcache在combinedcache中的占比

  <hbase.bucketcache.combinedcache.percentage>0.9</hbase.bucketcache.combinedcache.percentage>

  offheap模式

  <hbase.bucketcache.ioengine>offheap</hbase.bucketcache.ioengine>

  <hbase.bucketcache.size>0.4</hbase.bucketcache.size>

  <hbase.bucketcache.combinedcache.percentage>0.9</hbase.bucketcache.combinedcache.percentage>

  file模式

  <hbase.bucketcache.ioengine>file:/cache_path</hbase.bucketcache.ioengine>

  //bucketcache緩存空間大小,機關為MB

  <hbase.bucketcache.size>10 * 1024</hbase.bucketcache.size>

  //高速緩存路徑

  <hbase.bucketcache.persistent.path>file:/cache_path</hbase.bucketcache.persistent.path>

  總結

  HBase中緩存的設定對随機讀寫性能至關重要,本文通過對LRUBlockCache和BucketCache兩種方案的實作進行介紹,希望能夠讓各位看官更加深入地了解BlockCache的工作原理。BlockCache系列經過兩篇内容的介紹,基本已經解析完畢,那在實際線上應用中到底應該選擇哪種方案呢?下一篇文章讓我們一起看看HBase社群釋出BlockCache報告!

  更多技術分享,請關注網易視訊雲官方網站或者網易視訊雲官方微信(vcloud163)進行交流與咨詢

繼續閱讀