HBase 性能調優第一彈:記憶體篇
- 2020 年 3 月 11 日
- 筆記
這是使用 HBase 最不可避免的一個話題,就是 HBase 的性能調優,而且通常建立在我們對 HBase 內部運行機制比較了解的基礎上進行的,因此無論怎麼說,調優這塊都是一個相對複雜的事情。這一篇我們先來介紹與 HBase 記憶體最相關的調優內容。
1. 合理配置 JVM 記憶體
這裡首先涉及 HBase 服務的堆記憶體設置。一般剛部署的 HBase 集群,默認配置只給 Master 和 RegionServer 分配了 1G 的記憶體,RegionServer 中 MemStore 默認占 0.4 即 400MB 左右的空間,而一個 MemStore 刷寫閾值默認 128M,所以一個 RegionServer 也就能正常管理 3 個Region,多了就可能會產生小文件了,另外也容易發生 Full GC。因此建議合理調整 Master 和 RegionServer 的記憶體,比如:
export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -Xms8g -Xmx8g" export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -Xms32g -Xmx32g"
這裡也要根據實際集群資源進行配置,另外要牢記至少留 10% 的記憶體給作業系統使用。
2. 選擇合適的 GC 策略
另一個重要方面是 HBase JVM 的 GC 優化,其實 HBase 讀寫路徑上的很多設計都是圍繞 GC 優化做的。選擇合適的 GC 策略非常重要,對於 HBase 而言通常有兩種可選 GC 方案:
- ParallelGC 和 CMS 組合
- G1GC
而 CMS 和 G1 有什麼區別呢?
CMS 避免不了 Full GC,而且 Full GC 場景下會通過一次串列的完整垃圾收集來回收碎片化的記憶體,這個過程通常會比較長,應用執行緒會發生長時間的 STW 停頓,不響應任何請求;而 G1 適合大記憶體的場景,通過把堆記憶體劃分為多個 Region(不是 HBase 中的 Region),然後對各個 Region 單獨進行 GC,這樣就具有了並行整理記憶體碎片的功能,可以最大限度的避免 Full GC 的到來,提供更加合理的停頓時間。
由於 Master 只是做一些管理操作,實際處理讀寫請求和存儲數據的都是 RegionServer 節點,所以一般記憶體問題都出在 RegionServer 上。
這裡給的建議是,小堆(4G及以下)選擇 CMS,大堆(32G及以上)考慮用 G1,如果堆記憶體介入 4~32G 之間,可自行測試下兩種方案。剩下來的就是 GC 參數調優了,這一塊也要合理配置加上實際測試,後面再單獨聊這塊。
3. 開啟 MSLAB 功能
這是 HBase 自己實現了一套以 MemStore 為最小單元的記憶體管理機制,稱為 MSLAB(MemStore-Local Allocation Buffer),主要作用是為了減少記憶體碎片化,改善 Full GC 發生的情況。
MemStore 會在內部維護一個 2M 大小的 Chunk 數組,當寫入數據時會先申請 2M 的 Chunk,將實際數據寫入該 Chunk中,當該 Chunk 滿了以後會再申請一個新的 Chunk。這樣 MemStore Flush 後會達到粗粒度化的記憶體碎片效果,可以有效降低 Full GC 的觸發頻率。
HBase 默認是開啟 MSLAB 功能的,和 MSLAB 相關的配置包括:
- hbase.hregion.memstore.mslab.enabled:MSLAB 開關,默認為 true,即打開 MSLAB。
- hbase.hregion.memstore.mslab.chunksize:每個 Chunk 的大 小,默認為 2MB,建議保持默認值。
- hbase.hregion.memstore.chunkpool.maxsize:內部 Chunk Pool 功能,默認為 0 ,即關閉 Chunk Pool 功能。設置為大於 0 的值才能開啟,取值範圍為 [0,1],表示 Chunk Pool 占整個 MemStore 記憶體大小的比例。
- hbase.hregion.memstore.chunkpool.initialsize:表示初始化時申請多少個 Chunk 放到 Chunk Pool 中,默認為 0,即初始化時不申請 Chuck,只在寫入數據時才申請。
- hbase.hregion.memstore.mslab.max.allocation:表示能放入 MSLAB 的最大單元格大小,默認為 256KB,超過該大小的數據將從 JVM 堆分配空間而不是 MSLAB。
出於性能優化考慮,建議檢查相關配置,確保 MSLAB 處於開啟狀態。
4. 考慮開啟 BucketCache
這塊涉及到讀快取 BlockCache 的策略選擇。首先,BlockCache 是 RegionServer 級別的,一個 RegionServer 只有一個 BlockCache。BlockCache 的工作原理是讀請求會首先檢查 Block 是否存在於 BlockCache,存在就直接返回,如果不存在再去 HFile 和 MemStore 中獲取,返回數據時把 Block 快取到 BlockCache 中,後續同一請求或臨近查詢可以直接從 BlockCache 中獲取,避免過多的昂貴 IO 操作。BlockCache 默認是開啟的。
目前 BlockCache 的實現方案有三種:
LRUBlockCache
最早的 BlockCache 方案,也是 HBase 目前默認的方案。LRU 是 Least Recently Used 的縮寫,稱為近期最少使用演算法。LRUBlockCache 參考了 JVM 分代設計的思想,採用了快取分層設計。
LRUBlockCache 將整個 BlockCache 分為 single-access(單次讀取區)、multi-access(多次讀取區)和 in-memory 三部分,默認分別占讀快取的25%、50%、25%。其中設置 IN_MEMORY=true 的列族,Block 被讀取後才會直接放到 in-memory 區,因此建議只給那些數據量少且訪問頻繁的列族設置 IN_MEMORY 屬性。另外,HBase 元數據比如 meta 表、namespace 表也都快取在 in-memory 區。
SlabCache
HBase 0.92 版本引入的一種方案,起初是為了避免 Full GC 而引入的一種堆外記憶體方案,並與 LRUBlockCache 搭配使用,後來發現它對 Full GC 的改善很小,以至於這個方案基本被棄用了。
BucketCache
HBase 0.96 版本引入的一種方案,它借鑒了 SlabCache 的設計思想,是一種非常高效的快取方案。實際應用中,HBase 將 BucketCache 和 LRUBlockCache 搭配使用,稱為組合模式(CombinedBlockCahce),具體地說就是把不同類型的 Block 分別放到 LRUBlockCache 和 BucketCache 中。
HBase 會把 Index Block 和 Bloom Block 放到 LRUBlockCache 中,將 Data Block 放到 BucketCache 中,所以讀取數據會去 LRUBlockCache 查詢一下 Index Block,然後再去 BucketCache 中查詢真正的數據。
BucketCache 涉及的常用參數有:
- hbase.bucketcache.ioengine:使用的存儲介質,可設置為 heap、offheap 或 file,其中 heap 表示空間從JVM堆中申請,offheap 表示使用 DirectByteBuffer 技術實現堆外記憶體管理,file 表示使用類似 SSD 等存儲介質快取數據。默認值為空,即關閉 BucketCache,一般建議開啟 BucketCache。此外,HBase 2.x 不再支援 heap 選型。
- hbase.bucketcache.combinedcache.enabled:是否打開 CombinedBlockCache 組合模式,默認為 true。此外,HBase 2.x 不再支援該參數。
- hbase.bucketcache.size:BucketCache 大小,取值有兩種,一種是[0,1]之間的浮點數值,表示佔總記憶體的百分比,另一種是大於1的值,表示佔用記憶體大小,單位 MB。
根據上面的分析,一般建議開啟 BucketCache,綜合考慮成本和性能,建議比較合理的介質是:LRUBlockCache 使用記憶體,BuckectCache 使用SSD,HFile 使用機械磁碟。
5. 合理配置讀寫快取比例
HBase 為了優化性能,在讀寫路徑上分別設置了讀快取和寫快取,參數分別是 hfile.block.cache.size 與 hbase.regionserver.global.memstore.size,默認值都是 0.4,表示讀寫快取各占 RegionServer 堆記憶體的 40%。
在一些場景下,我們可以適當調整兩部分比例,比如寫多讀少的場景下我們可以適當調大寫快取,讓 HBase 更好的支援寫業務,相反類似,總之兩個參數要配合調整。
6. 總結
本文總結了與 HBase 記憶體最相關的調優內容,主要包括 JVM 記憶體大小設置,選擇合適的 GC 策略,建議開啟 MSLAB 與 BucketCache,以及合理配置讀寫快取比例等內容,希望通過本文我們對於 HBase 性能調優有了一定的認識。