HBase上关于CMS、GC碎片、大缓存的一种解决方案:Bucket Cache
介紹BucketCache前,先對HBase的Cache做個介紹:?
一.HBase在讀取時,會以Block為單位進行cache,用來提升讀的性能;?
二.Block可以分類為DataBlock(默認大小64K,存儲KV)、BloomBlock(默認大小128K,存儲BloomFilter數據)、IndexBlock(默認大小128K,索引數據,用來加快Row所在DataBlock的定位)?
三.對于一次隨機讀,Block的訪問順序為BloomBlock、IndexBlock、DataBlock,如果Region下面的StoreFile數目為2個,那么一次隨機讀至少訪問2次BloomBlock+1次IndexBlock+1次DataBlock?
四.我們通常將BloomBlock和IndexBlock統稱為MetaBlock,MetaBlock線上系統中基本命中率都是100%?
五.Block的cache命中率對HBase的讀性能影響十分大,所以DataBlockEncoding將KV在內存中進行壓縮,對于單行多列和Row相似的場景,可以提高內存使用率,增加讀性能?
六.HBase中管理緩存的Block的類為BlockCache,其實現目前主要是下面三種:?
6.1 LruBlockCache,默認的BlockCache實現,也是目前使用的BlockCache,使用一個HashMap維護Block Key到Block的映射,采用嚴格的LRU算法來淘汰Block,初始化時會指定容量大小,當使用量達到85%的時候開始淘汰block至75%的比例。?
優點:直接采用jvm提供的HashMap來管理Cache,簡單可依賴;內存用多少占多少,JVM會幫你回收淘汰的BlOCK占用的內存?
缺點:?
1.一個Block從被緩存至被淘汰,基本就伴隨著Heap中的位置從New區晉升到Old區?
2.晉升在Old區的Block被淘汰后,最終由CMS進行垃圾回收,隨之帶來的是Heap碎片?
3.因為碎片問題,隨之而來的是GC時晉升失敗的FullGC,我們的線上系統根據不同的業務特點,因為這個而發生FullGC的頻率,有1天的,1周的,1月半年的都有。對于高頻率的,?
在運維上通過在半夜手工觸發FullGC來緩解?
4.如果緩存的速度比淘汰的速度快,很不幸,現在的代碼有OOM的風險(這個可以修改下代碼避免)?
6.2 SlabCache,針對LruBlockCache的碎片問題一種解決方案,使用堆外內存,處于實驗性質,真實測試后,我們定位為不可用。說下它的原理:它由多個SingleSizeCache組成(所謂SingleSizeCache,就是只緩存固定大小的block,其內部維護一個ByteBuffer List,每個ByteBuffer的空間都是一樣的,比如64K的SingleSizeCache,ByteBuffer的空間都是64K,cache Block時把Block的內容復制到ByteBuffer中,所以block的大小必須小于等于64K才能被這個SingleSizeCache緩存;淘汰block的時候只需要將相應的ByteBuffer標記為?
空閑,下次cache的時候對其上的內存直接進行覆蓋就行了),cache Block的時候,選擇一個小于且最接近的SingleSizeCache進行緩存,淘汰block亦此。由于SingleSize的局限性,其使用上和LruBlockCache搭配使用,叫做DoubleBlockCache,cache block的時候LruBlockCache和SlabCache都緩存一份,get block的時候順序為LruBlockCache、SlabCache,如果只有SlabCache命中,那么再將block緩存到LruBlockCache中(本人覺得它的這個設計很費,你覺得呢)?
優點:其思想:申請固定內存空間,Block的讀寫都在這片區域中進行?
缺點:?
1.cache block和 get block的時候,需要內存復制?
2.SingleSizeCache的設計,導致內存使用率很低?
3.與LruBlockCache搭配使用不合理,導致所有的block都會去LruBlockCache中逗留一下,結果是CMS和碎片都不能有所改善?
6.3 BucketCache,可以看成是對SlabCache思想在實現上的一種改進及功能擴展,其優點是解決LruBlockCache的缺點及支持面向高性能讀的大緩存空間.?
1.何謂大緩存?緩存Block的存儲介質不再僅僅依賴在內存上,而是可以選擇為Fusion-io、SSD等高速磁盤,我們稱之為二級緩存?
2.何謂Bucket?我們將緩存空間劃分為一個個的Bucket,每個Bucket都貼上一個size標簽,將Block緩存在最接近且小于size的bucket中(和SingleSizeCache很相似)?
3.怎么解決CMS 碎片問題?Block存儲在Bucket中,而每個Bucket的物理存儲是不變的,也就是說系統剛啟動的時候,我們就申請了一堆Bucket內存空間,而這些內存空間是一直在Old區,block的Get/Cache動作只是對這片空間的訪問/覆寫,CMS/碎片自然大大減少?
4.怎么使用?上面的描述指出BucketCache可以有兩種用法:?
4.1 與LruBlockCache搭配,作為主要的內存cache方案使用?
?
4.2 作為二級緩存使用,將Block緩存在我們的高速盤(Fusion-IO)中?
?
5.BucketCache中的Cache/Get Block邏輯??
?
?
簡單地描述下:?
CacheBlock的時候,將Block放在一個RAMMap和一個Queue中,然后WriterThread異步從Queue中remove Block寫入到IOEngine(內存或高速盤)中,并將BlockKey及其位置、長度等信息記錄在backingMap?
GetBlock的時候,先訪問RAMMap,然后訪問backingMap獲取block的位置及長度,從IOEngine讀取數據?
6.Block在IOEngine中的位置是怎么分配的??
?
我們將物理空間劃分為一堆等大的Bucket,每一個Bucket有一個序號及一個size標簽,于是Block所在bucket的序號及其在bucket中的offset與block在物理空間的offset就形成了一一對應。我們通過BucketAllocator為指定大小的Block尋找一個Bucket進行存放,于是就得到了其在物理空間上的位置。?
上圖描述了BucketAllocator對于Bucket的組織管理:?
6.1?每個Bucket都有一個size標簽,目前對于size的分類,是在啟動時候就確定了,如默認的有(8+1)K、(16+1)K、(32+1)K、(40+1)K、(48+1)K、(56+1)K、(64+1)K、(96+1)K ... (512+1)K?
6.2?相同size標簽的Bucket由同一個BucketSizeInfo管理?
6.3?Bucket的size標簽可以動態調整,比如64K的block數目比較多,65K的bucket被用完了以后,其他size標簽的完全空閑的bucket可以轉換成為65K的bucket,但是至少保留一個該size的bucket?
6.4?如果最大size的bucket為513K,那么超過這個大小的block無法存儲,直接拒絕?
6.5?如果某個size的bucket用完了,那么會依照LRU算法觸發block淘汰?
問題:?
6.6.如果系統一開始都是某個size的block,突然變成另外個size的block(不能存在同個size的bucket中),根據6.5不是會不停地進行淘汰算法??
是的,但是由于淘汰是異步的,影響不大,而且隨著淘汰進行,bucket的大小會逐漸向那個block size大小bucket轉移,最終穩定?
6.7?BucketAllocator中allocate block的流程??
?
?
6.8?BucketAllocator中free block的流程??
?
6.9?第一種使用的測試結果?
?
6.10?第二種使用的測試結果?
?
6.11?更多細節,盡在代碼中?
https://issues.apache.org/jira/browse/HBASE-7404?
總結
以上是生活随笔為你收集整理的HBase上关于CMS、GC碎片、大缓存的一种解决方案:Bucket Cache的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java并发包系列---LockSupp
- 下一篇: Hbase过滤器(Filter)汇总