和其餘數據庫同樣,優化IO也是HBase提高性能的不二法寶,而提供緩存更是優化的重中之重。html
根據二八法則,80%的業務請求都集中在20%的熱點數據上,所以將這部分數據緩存起就能夠極大地提高系統性能。算法
HBase在實現中提供了兩種緩存結構:MemStore和BlockCache。其中MemStore稱爲寫緩存,HBase執行寫操做首先會將數據寫入MemStore,並順序寫入HLog,等知足必定條件後統一將MemStore中數據刷新到磁盤,這種設計能夠極大地提高HBase的寫性能。不只如此,MemStore對於讀性能也相當重要,假如沒有MemStore,讀取剛寫入的數據就須要從文件中經過IO查找,這種代價顯然是昂貴的!BlockCache稱爲讀緩存,HBase會將一次文件查找的Block塊緩存到Cache中,以便後續同一請求或者鄰近數據查找請求,能夠直接從內存中獲取,避免昂貴的IO操做。數據庫
瞭解BlockCache中存放的內容,能夠幫助咱們更好設計BlockCache的大小。緩存
Your data: Each time a Get or Scan operation occurs, the result is added to the BlockCache if it was not already cached there. If you use the BucketCache, data blocks are always cached in the BucketCache.異步
Row keys: When a value is loaded into the cache, its row key is also cached. This is one reason to make your row keys as small as possible. A larger row key takes up more space in the cache.jvm
hbase:meta: The hbase:meta catalog table keeps track of which RegionServer is serving which regions. It can consume several megabytes of cache if you have a large number of regions, and has in-memory access priority, which means HBase attempts to keep it in the cache as long as possible.源碼分析
Indexes of HFiles: HBase stores its data in HDFS in a format called HFile. These HFiles contain indexes which allow HBase to seek for data within them without needing to open the entire HFile. The size of an index is a factor of the block size, the size of your row keys, and the amount of data you are storing. For big data sets, the size can exceed 1 GB per RegionServer, although the entire index is unlikely to be in the cache at the same time. If you use the BucketCache, indexes are always cached on-heap.性能
Bloom filters: If you use Bloom filters, they are stored in the BlockCache. If you use the BucketCache, Bloom filters are always cached on-heap.測試
The sum of the sizes of these objects is highly dependent on your usage patterns and the characteristics of your data. For this reason, the HBase Web UI and Cloudera Manager each expose several metrics to help you size and tune the BlockCache.優化
將上面的緩存分爲兩類:
當使用BucketCache,數據緩存的最小單位是Block。
BlockCache是Region Server級別的,一個Region Server只有一個Block Cache,在Region Server啓動的時候完成Block Cache的初始化工做。到目前爲止,HBase前後實現了3種Block Cache方案,LRUBlockCache是最初的實現方案,也是默認的實現方案;HBase 0.92版本實現了第二種方案SlabCache,見HBASE-4027;HBase 0.96以後官方提供了另外一種可選方案BucketCache,見HBASE-7404。
LRU緩存把最近最少使用的數據移除,讓給最新讀取的數據。而每每最常讀取的,也是讀取次數最多的,因此,利用LRU緩存,咱們可以提升系統的performance.
LRUBlockCache將緩存分爲三塊:single-access區、mutil-access區、in-memory區,分別佔到整個BlockCache大小的25%、50%、25%。
memory區表示數據能夠常駐內存,通常用來存放訪問頻繁、數據量小的數據,好比元數據,用戶也能夠在建表的時候經過設置列族屬性IN-MEMORY= true將此列族放入in-memory區。很顯然,這種設計策略相似於JVM中young區、old區以及perm區。不管哪一個區,系統都會採用嚴格的Least-Recently-Used算法
LruBlockCache內部是經過一個ConcurrentHashMap來保存全部cache的block的。
/** Concurrent map (the cache) */ private final Map<BlockCacheKey,LruCachedBlock> map; map = new ConcurrentHashMap<BlockCacheKey,LruCachedBlock>(mapInitialSize, mapLoadFactor, mapConcurrencyLevel);
使用Java NIO DirectByteBuffer技術實現了堆外內存存儲。
SlabCache有兩個緩存區,分別佔整個BlockCache大小的80%和20%,每一個緩存區分別存儲固定大小的Block塊:
前者主要存儲小於等於64K大小的Block,後者存儲小於等於128K Block,若是一個Block太大就會致使兩個區都沒法緩存。
Q: 用戶設置BlockSize = 256K怎麼辦?
HBase實際實現中將SlabCache和LRUBlockCache搭配使用,稱爲DoubleBlockCache。一次隨機讀中,一個Block塊從HDFS中加載出來以後會在兩個Cache中分別存儲一份;緩存讀時首先在LRUBlockCache中查找,若是Cache Miss再在SlabCache中查找,此時若是命中再將該Block放入LRUBlockCache中。
SlabCache存在的問題?
SlabCache設計中固定大小內存設置會致使實際內存使用率比較低,
並且使用LRUBlockCache緩存Block依然會由於JVM GC產生大量內存碎片。
每一個bucket會有一個baseoffset變量和一個size標籤,其中baseoffset變量表示這個bucket在實際物理空間中的起始地址,所以block的物理地址就能夠經過baseoffset和該block在bucket的偏移量惟一肯定;而size標籤表示這個bucket能夠存放的block塊的大小。
默認14個不一樣大小的Bucket :4, 8, 16, 32, 40, 48, 56, 64, 96, 128, 192, 256, 384, 512 KB
優勢:能夠緩存不一樣大小的數據塊Block。
其中實線表示cache block流程,虛線表示get block流程。
BucketCache默認有三種工做模式:
heap、offheap和file;
三者不一樣之處是對應的最終存儲介質有所不一樣,即上述所講的IOEngine有所不一樣。
heap模式和offheap模式,使用內存做爲最終存儲介質。
分配內存時,offheap快;讀內存時,heap快。heap模式受GC影響
file模式
它使用Fussion-IO或者SSD等做爲存儲介質,相比昂貴的內存,這樣能夠提供更大的存儲容量,所以能夠極大地提高緩存命中率。
<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>
<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>
<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>
在’緩存所有命中’場景下,LRU君可謂完勝CBC君。所以若是總數據量相比JVM內存容量很小的時候,選擇LRU君;
在全部其餘存在緩存未命中狀況的場景下, LRU君的GC性能幾乎只有CBC君的1/3,而吞吐量、讀寫延遲、IO、CPU等指標二者基本至關,所以建議選擇CBC。
之因此在’緩存所有命中’場景下LRU的各項指標完勝CBC,而在’緩存大量未命中’的場景下,LRU各項指標與CBC基本至關,是由於HBase在讀取數據的時候,若是都緩存命中的話,對於CBC,須要將堆外內存先拷貝到JVM內,而後再返回給用戶,流程比LRU君的堆內內存複雜,延遲就會更高。而若是大量緩存未命中,內存操做就會佔比很小,延遲瓶頸主要在於IO,使得LRU和CBC二者各項指標基本至關。
1)若是請求的數據比較符合緩存,命中率比較高,使用LRUBlockCache方式會比CombinedBlockCache的吞吐量高上20%(但也會犧牲一些垃圾回收)。
1)若是須要緩存的數據超過堆大小的狀況下,推薦使用Block Cache下的off-heap。
2)當scan獲取數據時,能夠經過setCacheBlocks方法來設置是否使用block cache,對於頻繁訪問的行才建議使用block cache。
3)對於MapReduce的Scan做爲輸入任務,應該設置爲setCacheBlocks(false)。
4)若是緩存遇到持續高的驅逐速率,這會致使LruBlockCache大量的垃圾回收,請使用CombinedBlockCache。
5)CombinedBlockCache在固態磁盤上使用file文件模式具備更好的垃圾回收,但吞吐量低於CombinedBlockCache使用offheap模式。
BlockCache先使用默認參數LRUBlockCache
最佳參數:根據監控指標作優化,加大內存併兼顧GC時間。