下面介紹Hbase的緩存機制:
a.HBase在讀取時,會以Block爲單位進行cache,用來提高讀的性能算法
b.Block能夠分類爲DataBlock(默認大小64K,存儲KV)、BloomBlock(默認大小128K,存儲BloomFilter數據)、IndexBlock(默認大小128K,索引數據,用來加快Rowkey所在DataBlock的定位)緩存
c.對於一次隨機讀,Block的訪問順序爲BloomBlock、IndexBlock、DataBlock,若是Region下面的StoreFile數目爲2個,那麼一次隨機讀至少訪問2次BloomBlock+1次IndexBlock+1次DataBlock運維
d.咱們一般將BloomBlock和IndexBlock統稱爲MetaBlock,MetaBlock線上系統中基本命中率都是100%異步
e.Block的cache命中率對HBase的讀性能影響十分大,因此DataBlockEncoding將KV在內存中進行壓縮,對於單行多列和Row類似的場景,能夠提升內存使用率,增長讀性能jvm
f.HBase中管理緩存的Block的類爲BlockCache,其實現目前主要是下面三種,下面將着重介紹這三類Cache 性能
默認的BlockCache實現,也是目前使用的BlockCache,使用一個HashMap維護Block Key到Block的映射,採用嚴格的LRU算法來淘汰Block,初始化時會指定容量大小,當使用量達到85%的時候開始淘汰block至75%的比例。 測試
優勢:直接採用jvm提供的HashMap來管理Cache,簡單可依賴;內存用多少佔多少,JVM會幫你回收淘汰的BlOCK佔用的內存 spa
缺點: 設計
(1)一個Block從被緩存至被淘汰,基本就伴隨着Heap中的位置從New區晉升到Old區
(2)晉升在Old區的Block被淘汰後,最終由CMS進行垃圾回收,隨之帶來的是Heap碎片
(3)由於碎片問題,隨之而來的是GC時晉升失敗的FullGC,咱們的線上系統根據不一樣的業務特色,由於這個而發生FullGC的頻率,有1天的,1周的,1月半年的都有。對於高頻率的,在運維上經過在半夜手工觸發FullGC來緩解
(4)若是緩存的速度比淘汰的速度快,很不幸,如今的代碼有OOM的風險(這個能夠修改下代碼避免) orm
針對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和碎片都不能有所改善
能夠當作是對SlabCache思想在實現上的一種改進及功能擴展,其優勢是解決LruBlockCache的缺點及支持面向高性能讀的大緩存空間,下面將着重介紹它的功效
3.1 何謂大緩存?
緩存Block的存儲介質再也不僅僅依賴在內存上,而是能夠選擇爲Fusion-io、SSD等高速磁盤,咱們稱之爲二級緩存
3.2 何謂Bucket?
咱們將緩存空間劃分爲一個個的Bucket,每一個Bucket都貼上一個size標籤,將Block緩存在最接近且小於size的bucket中(和SingleSizeCache很類似)
3.3 怎麼解決CMS 碎片問題?
Block存儲在Bucket中,而每一個Bucket的物理存儲是不變的,也就是說系統剛啓動的時候,咱們就申請了一堆Bucket內存空間,而這些內存空間是一直在Old區,block的Get/Cache動做只是對這片空間的訪問/覆寫,CMS/碎片天然大大減小
3.4 怎麼使用?BucketCache能夠有兩種用法
3.4.1 與LruBlockCache搭配,做爲主要的內存cache方案使用
在hbase-site.xml中設置如下參數:
– 「hbase.bucketcache.ioengine」 「heap」
– 「hbase.bucketcache.size」 0.4(bucket cache的大小, 0.4是最大對內存的比例)
– 可選配置
• 「hbase.bucketcache.combinedcache.percentage」 默認是0.9f (在CombinedCache中的比例)
3.4.2 做爲二級緩存使用,將Block緩存在咱們的高速盤(Fusion-IO)中
在hbase-site.xml中設置如下參數:
– 「hbase.bucketcache.ioengine」 「file:/disk1/hbase/cache.data」(存儲block數據的路徑)
– 「hbase.bucketcache.size」 10*1024 (bucket cache的大小, 單位是MB, 10*1024 是10GB)
– 「hbase.bucketcache.combinedcache 「 false
– 可選配置
• 「hbase.bucketcache.persistent.path」 「file:/disk1/hbase/cache.meta」(存儲bucket cache的元數據的路徑, 用於啓動的時候恢復cache)
3.5.BucketCache中的Cache/Get Block邏輯?
簡單地描述下: CacheBlock的時候,將Block放在一個RAMMap和一個Queue中,而後WriterThread異步從Queue中remove Block寫入到IOEngine(內存或高速盤)中,並將BlockKey及其位置、長度等信息記錄在backingMap GetBlock的時候,先訪問RAMMap,而後訪問backingMap獲取block的位置及長度,從IOEngine讀取數據
3.6.Block在IOEngine中的位置是怎麼分配的?
咱們將物理空間劃分爲一堆等大的Bucket,每個Bucket有一個序號及一個size標籤,因而Block所在bucket的序號及其在bucket中的offset與block在物理空間的offset就造成了一一對應。咱們經過BucketAllocator爲指定大小的Block尋找一個Bucket進行存放,因而就獲得了其在物理空間上的位置。
上圖描述了BucketAllocator對於Bucket的組織管理:
(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
(2) 相同size標籤的Bucket由同一個BucketSizeInfo管理
(3) Bucket的size標籤能夠動態調整,好比64K的block數目比較多,65K的bucket被用完了之後,其餘size標籤的徹底空閒的bucket能夠轉換成爲65K的bucket,可是至少保留一個該size的bucket
(4)若是最大size的bucket爲513K,那麼超過這個大小的block沒法存儲,直接拒絕
(5)若是某個size的bucket用完了,那麼會依照LRU算法觸發block淘汰
問題:
若是系統一開始都是某個size的block,忽然變成另外個size的block(不能存在同個size的bucket中)會發生什麼,是否還會不停地進行淘汰算法?
是的,可是因爲淘汰是異步的,影響不大,並且隨着淘汰進行,bucket的大小會逐漸向那個block size大小bucket轉移,最終穩定
3.7 BucketAllocator中allocate block的流程?
3.8 BucketAllocator中free block的流程?
3.9 第一種使用的測試結果
3.10 第二種使用的測試結果