HBase最佳實踐-內存規劃
線上HBase集群應該如何進行參數配置?這其實是很多HBase初學者在實踐環節都可能會遇到的問題,有些人會選擇默認配置,有些人會選擇其他公司的推薦配置;誠然,這樣的參數配置在大多數情況下都能正常工作,但性能卻未必最佳、資源未必都能被合理利用。本文結合筆者的實踐經驗,針對不同應用場景,對多種工作模式下的參數進行詳細說明,并結合相關示例對集群規劃中最核心模塊-內存規劃進行介紹。一方面希望讀者能夠了解HBase內存相關知識細節,另一方面能夠將這些知識應用于實踐、不斷對集群進行優化。
HBase中內存規劃直接涉及讀緩存BlockCache、寫緩存MemStore,影響系統內存利用率、IO利用率等資源以及讀寫性能等,重要性不言而喻。主要配置也是針對BlockCache和MemStore進行,然而針對不同業務類型(簡單說來主要包括讀多寫少型和寫多讀少型),內存的相關配置卻完全不同。再者,對于讀緩存BlockCache,線上一般會有兩種工作模式:LRUBlockCache和BucketCache,不同工作模式下的相關配置也不盡相同。為了比較完整的說明不同應用場景以及不同緩存工作模式的內存規劃,下文會分分別介紹兩個案列:讀多寫少型+BucketCache,寫多讀少型+LRUBlockCache。
需要說明的是,業務類型和讀緩存工作模式之間沒有任何直接的關聯。業務到底使用BucketCache還是使用LRUBlockCache,只和分配給RegionServer的內存大小有關。一般而言,如果HBASE_HEAPSIZE > 20G,選擇BucketCache,否則選擇LRUBlockCache( 參考hortonworks文檔 ),理論依據可以參考 這里 。
案例一:寫多讀少型 + LRUBlockCache
內存分布圖
在詳細說明具體的容量規劃前,首先需要明確LRUBlockCache模式下的內存分布圖,如下圖所示:
圖中分配給RegionServer進程的內存就是JVM內存,主要分為三部分:LRUBlockCache,用于讀緩存;MemStore,用于寫緩存;Other,用于RS運行所必須的其他對象;
內存規劃思路
了解了BucketCache模式下的內存分布圖之后,我們具體來分析如何規劃內存,首先列出來基本條件:
a. 整個物理機內存:96G
b. 業務負載分布:30%讀,70%寫
接下來將問題一步一步分解,從上至下按照邏輯對內存進行規劃:
(1) 系統內存基礎上如何規劃RS內存?
這個問題需要根據自身服務器情況決定,一般情況下,在不影響其他服務的情況下,越大越好。我們目前設置為64G,為系統內存的2/3。
(2) 如何設置LRUBlockCache、MemStore?
確定RegionServer總內存之后,接下來分別規劃LRUBlockCahce和MemStore的總內存。在此需要考慮兩點:在寫多讀少的業務場景下,寫緩存顯然應該分配更多內存,讀緩存相對分配更少;HBase在此處有個硬規定:LRUBlockCache + MemStore < 80% * JVM_HEAP,否則RS無法啟動。
推薦內存規劃: MemStore = 45% * JVM_HEAP = 64G * 45% = 28.8G ,LRUBlockCache = 30% * JVM_HEAP = 64G * 30% = 19.2G; 默認情況下Memstore為40% * JVM_HEAP,而LRUBlockCache為25% * JVM_HEAP
配置設置實踐
(1)設置JVM參數如下:
-XX:SurvivorRatio=2 -XX:+PrintGCDateStamps -Xloggc:$HBASE_LOG_DIR/gc-regionserver.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=1 -XX:GCLogFileSize=512M -server -Xmx64g -Xms64g -Xmn4g -Xss256k -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+UseParNewGC -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+CMSClassUnloadingEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75 -XX:-DisableExplicitGC
(2)hbase-site.xml中MemStore相關參數設置如下:
<property> <name>hbase.regionserver.global.memstore.upperLimit</name> <value>0.45</value> </property> <property> <name>hbase.regionserver.global.memstore.lowerLimit</name> <value>0.40</value> </property>
由上述定義可知,hbase.regionserver.global.memstore.upperLimit設置為0.45,hbase.regionserver.global.memstore.lowerLimit設置為0.40hbase.regionserver.global.memstore.upperLimit表示RegionServer中所有MemStore占有內存在JVM內存中的比例上限。如果所占比例超過這個值,RS會首先將所有Region按照MemStore大小排序,并按照由大到小的順序依次執行flush,直至所有MemStore內存總大小小于hbase.regionserver.global.memstore.lowerLimit,一般lowerLimit比upperLimit小5%。
(3)hbase-site.xml中LRUBlockCache相關參數設置如下:
<property> <name>hfile.block.cache.size</name> <value>0.3</value> </property>
hfile.block.cache.size表示LRUBlockCache占用內存在JVM內存中的比例,因此設置為0.3
案例二:讀多寫少型 + BucketCache
內存分布圖
與LRUBlockCache模式相比,BucketCache模式下的內存分布圖會更加復雜,如下圖所示:
~
如圖,整個RegionServer內存(Java進程內存)分為兩部分:JVM內存和堆外內存。其中JVM內存中LRUBlockCache和堆外內存BucketCache一起構成了讀緩存CombinedBlockCache,用于緩存讀到的Block數據,其中LRUBlockCache用于緩存元數據Block,BucketCache用于緩存實際用戶數據Block;MemStore用于寫流程,緩存用戶寫入KeyValue數據;還有部分用于RegionServer正常運行所必須的內存;
內存規劃思路
和案例一相同,本案例中物理機內存也是96G,不過業務類型為讀多寫少:70%讀+30%寫
因為BucketCache模式下內存分布圖相對復雜,我們使用如下表格一步一步對內存規劃進行解析:
序號 |
步驟 |
原理 |
計算公式 |
計算值 |
修正值 |
A |
規劃RS總內存 |
在系統內存允許且不影響其他服務的情況下,越多越好。設置為系統總內存的 2/3。 |
2/3 * 96G |
64G |
64G |
B |
規劃讀緩存 CombinedBlockCache |
整個RS內存分為三部分:讀緩存、寫緩存、其他。基本按照5 : 4 : 1的分配原則。讀緩存設置為整個RS內存的50% |
A * 50% |
32G |
26G |
B1 |
規劃讀緩存LRU部分 |
LRU部分主要緩存數據塊元數據,數據量相對較小。設置為整個讀緩存的10% |
B * 10% |
3.2G |
3G |
B2 |
規劃讀緩存BucketCache部分 |
BucketCache部分主要緩存用戶數據塊,數據量相對較大。設置為整個讀緩存的90% |
B * 90% |
28.8G |
24G |
C |
規劃寫緩存MemStore |
整個RS內存分為三部分:讀緩存、寫緩存、其他。基本按照5:4:1的分配原則。寫緩存設置為整個RS內存的40% |
A * 40% |
25.6G |
25G |
D |
設置JVM_HEAP |
RS總內存大小 – 堆外內存大小 |
A – C |
35.2G |
40G |
計算修正
看到這里,可能很多仔細的朋友就會疑問,案例一不是說過HBase有一個硬規定么:LRUBlockCache + MemStore < 80% * JVM_HEAP,否則RS無法啟動。不錯,HBase確實有這樣一個規定,這個規定的本質是為了在內存規劃的時候能夠給除過寫緩存和讀緩存之外的其他對象留夠至少20%的內存空間。那按照上述計算方式能不能滿足這個硬規定呢,LRU + MemStore / JVM_HEAP = 3G + 25G / 35G = 28G / 35G = 80% ,剛好等于閾值 80%,為了更加保險,建議這個值在70%~75%之間,因此需要對計算值進行簡單的修正,適量增大JVM_HEAP值(增大至40G),BucketCache減少到24G(CombinedBlockCache需調整到26G)。調整之后,LRU + MemStore / JVM_HEAP = 3G + 25G / 40G = 28G / 40G = 70%
配置設置實踐
(1)設置JVM參數如下:
-XX:SurvivorRatio=2 -XX:+PrintGCDateStamps -Xloggc:$HBASE_LOG_DIR/gc-regionserver.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=1 -XX:GCLogFileSize=512M -server -Xmx40g -Xms40g -Xmn4g -Xss256k -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+UseParNewGC -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+CMSClassUnloadingEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75 -XX:-DisableExplicitGC
(2)hbase-site.xml中MemStore相關參數設置如下:
<property> <name>hbase.regionserver.global.memstore.upperLimit</name> <value>0.60</value> </property> <property> <name>hbase.regionserver.global.memstore.lowerLimit</name> <value>0.55</value> </property>
根據upperLimit參數的定義,結合上述內存規劃數據可計算出 upperLimit = 25G / 40G = 60%。因此upperLimit參數設置為0.60,lowerLimit設置為0.55
(3)hbase-site.xml中CombinedBlockCache相關參數設置如下:
<property> <name>hbase.bucketcache.ioengine</name> <value>offheap</value> </property> <property> <name>hbase.bucketcache.size</name> <value>26624</value> </property> <property> <name>hbase.bucketcache.percentage.in.combinedcache</name> <value>0.90</value> </property>
按照上述介紹設置之后,所有關于內存相關的配置基本就完成了。但是需要特別關注一個參數hfile.block.cache.size,這個參數在本案例中并不需要設置,沒有任何意義。但是HBase的硬規定卻是按照這個參數計算的,這個參數的值加上hbase.regionserver.global.memstore.upperLimit的值不能大于0.8,上文提到hbase.regionserver.global.memstore.upperLimit值設置為0.6,因此,hfile.block.cache.size必須設置為一個小于0.2的任意值。 hbase.bucketcache.ioengine表示bucketcache設置為offheap模式;hbase.bucketcache.size表示所有讀緩存占用內存大小,該值可以為內存真實值,單位為M,也可以為比例值,表示讀緩存大小占JVM內存大小比例。如果為內存真實值,則為26G,即26624M。而如果是比例值,則計算應為 26G / 40G = 65%,設為0.65即可;hbase.bucketcache.percentage.in.combinedcache參數表示用于緩存用戶數據塊的內存(堆外內存)占所有讀緩存的比例,設為0.90;
至此,關于HBase集群的內存規劃經過兩個案例的分析到此就要結束了,希望看官能夠理解整個分析的思路。結合自己的應用場景以及內存使用模式對內存進行規劃。本文也只是筆者個人見解,如果有任何意見可以相互交流討論
來自: http://hbasefly.com/2016/06/18/hbase-practise-ram/