HBase性能優化指南

垃圾回收優化

當region服務器處理大量的寫入負載時,繁重的任務會迫使JRE默認的內存分配策略沒法保證程序的穩定性
因此咱們可能須要對region服務器的垃圾回收機制進行一些參數調整(由於master並不處理實際任務,因此沒有優化的必要)php

首先來了解JAVA內存中的幾個概念web

HBase構架中咱們能夠知道
數據會被寫入到memstore內存中直到達到一個閾值以後刷寫持久化到磁盤
可是因爲數據是客戶端在不一樣時間寫入的,這些數據佔據的JAVA內存中的堆空間極可能是不連續的,因此JAVA虛擬機的內存會出現「孔洞」算法

在JAVA的內存空間中,數據在內存停留的時間決定了該數據在內存中的位置分配
被快速插入並刷寫到磁盤的數據會被分配到年輕代的堆中,這種空間能夠被迅速回收,對內存管理沒有太大影響,年輕代一般只佔用128-512M的內存空間
在內存中國停留時間過長(例如向一個列族中插入數據過慢的時候),該數據有可能被放在老生代的堆中,老生代會佔用幾乎能夠佔用的內存空間,一般是好幾個Gshell

region服務器的默認配置中,年輕代的空間大小對於大多數負載來講都過小
因此能夠將其適當的調大,若是使用默認值的話,頻繁的從年輕代中收集對象對消耗大量CPU,因此某些狀況下用戶可能會發現服務器CPU的使用量會急劇上升
用戶能夠經過hbase-env.sh中的HBASE_REGIONSERVER_OPT選項設置爲-Xmn128m來修改默認的配置,通常來講128M能夠知足需求,具體的配置還須要參考實際的JVM指標apache

所謂的垃圾回收指的是:爲了重複使用因爲數據刷寫到磁盤產生的內存孔洞,JRE將壓縮內存碎片使其再次被使用,同時將符合條件的年輕代數據提高到老生代等過程
推薦的垃圾回收策略是:-XX:+UseParNewGC -XX:UseConcMarkSweepGCapi

-XX:+UseParNewGC:
該選項設置年輕代使用Parallel New Conllector垃圾回收策略
效果是,清空年輕代堆的時候將中止運行的JAVA進程
難道中止進程不會對集羣有影響嗎?
對於年輕代來講基本是不會的,由於年輕代很小,這個過程花費的時間很短,一般只須要幾百毫秒的時間
可是對於老生代來講就不是這麼回事了,若是在回收老生代空間的時候中止了JAVA進程
一旦清理的時間超出了ZK的會話超時限制,當這個region回過神來的時候就會發現已經被master拋棄了,而後自行關閉。。
基於這個狀況,年輕代的堆大小也不能設置得太大,以避免垃圾回收的時候影響服務器的延遲緩存

因此對於老生代來講應該使用第二個選項的配置
-XX:UseConcMarkSweepGC:
該策略會視圖在不中止運行JAVA進程的狀況下儘量的異步並行完成垃圾回收的工做,避免由於垃圾回收形成的region停頓
可是缺點是將會增長CPU的負擔性能優化

基於以上幾點和gc日誌相關,可使用如下列內容做爲集羣的初始配置:bash

export HBASE_REGIONSERVER_OPT="-Xmx8g -Xms8g -Xmn128m -XX:+UseParNewGC -XX:UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -verbose:gc -XX:+printGCDetails -XX:+PrintGCTimeStamps -Xloggc:${HBASE_HOME}/logs/gc-${hostname}-hbase.log"

使用壓縮

HBase支持大量的壓縮算法,能夠從嫘祖級別上進行壓縮
由於CPU壓縮和解壓縮消耗的時間每每比從磁盤讀取和寫入數據要快得多
因此使用壓縮一般會帶來很可觀的性能提高服務器

可用的編解碼器

幾個壓縮算法的比較:

算法 壓縮比% 壓縮MB/s 解壓MB/s
GZIP 13.4 21 118
LZO 20.5 135 410
Zippy/Snappy 22.2 172 409

關於壓縮算法在HBase中的集成安裝請Google之~

驗證安裝

安裝完壓縮算法以後咱們可使用一些工具對算法進行測試

壓縮測試工具

HBase包含一個可以測試壓縮設置是否正常的工具,咱們能夠輸入

./bin/hbase org.apache.hadoop.hbase.util.CompresstionTest

用戶須要制定一個測試文件和壓縮算法,例如:

./bin/hbase org.apache.hadoop.hbase.util.CompresstionTest /user-home/test.gz gz

對於成功的安裝,將會返回success信息
反之則會獲得一個異常

啓用檢查

及時測試工具報告成功了,因爲JNI須要先安裝好本地庫,若是缺失這一步將會在添加新服務器的時候出現問題
致使新的服務器使用本地庫打開含有壓縮列族的region失敗

咱們能夠在服務器啓動的時候檢查壓縮庫是否已經正確安裝,若是沒有則不會啓動服務器:

<property>
    <name>hbase.regionserver.codecs</name>
    <value>snappy,lzo</value>
</property>

這樣一來region服務器在啓動的時候將會檢查Snappy和LZO壓縮庫是否已經正確安裝

啓用壓縮

咱們能夠經過shell建立表的時候指定列族的壓縮格式:

create 'testtable',{NAME => 'colfam1',COMPRESSION => 'GZ'}

須要注意的是,若是用戶要更改一個已經存在的表的壓縮格式,要先將該表disable才能修改以後再enable從新上線
而且更改後的region只有在刷寫存儲文件的時候纔會使用新的壓縮格式,沒有刷寫以前保持原樣
用戶能夠經過shell的major_compact 來強制格式重寫,可是此操做會佔用大量資源

優化拆分和合並

默認的拆分和合並機制是合理的,可是在某些狀況下咱們仍然能夠根據需求對這部分功能過呢進行優化以得到額外的性能

管理拆分

一般HBase是自動處理region拆分的:一旦它們增加到了既定的閾值region將被拆分爲兩個
以後它們能夠接受新的數據並繼續增加

可是有沒有這樣一種狀況:拆分以後的兩個子region都已恆定的速率增大,致使在同一時刻進行拆分
這種狀況是頗有可能出現的對吧,可是兩個region同時拆分會怎麼樣嗎?
只有兩個的話確實不會有什麼影響,可是若是兩個region拆分以後繼續以恆定的速率增加致使子子region又一塊兒拆分,甚至子子子region,無限循環…

這樣一來問題就大條了吧,這種狀況被稱爲拆分/合併風暴,這將致使磁盤IO的飆升

這種狀況下,與其依賴HBase的自動拆分,用戶不如手動使用split和major_compact命令來管理
由於手動管理的話能夠將這些region的拆分/合併時機分割開來,儘可能分散IO負載
碰到這種狀況的時候不要忘記把配置文件中的hbase.hregion.max.filesize設置爲很是大(例如100G?可是觸發的時候回致使一小時的major合併…)
來防止自定拆分/合併的發生

region熱點問題

HBase高級用法中咱們提到過行健的設計避免數據熱點問題的產生
可是在生產環境中,即便使用了隨機行健,某一臺的機器負載仍然要大於其餘機器
對於不少region表來講,大部分region分佈並不均勻,即大多數region位於同一服務器上

惟一能夠緩解這種現象的途徑是手動將熱點region按特定的便捷拆分出一個或者多個新region並負載分佈到多個region服務器上
用戶能夠爲region指定一個拆分行健,即region被拆分爲兩部分的位置
這個行健能夠任意指定,能夠生成大小徹底不一樣的region

預拆分region

HBase建表時默認只在一個HRegionServer上建一個HRegion,寫數據所有往該HRegion寫
當這個HRegion達到必定大小的時候進行水平切割爲多個HRegion,自動進行負載均衡

在這個環節,咱們能夠經過預先建立一個空的HRegion,並規定好每一個HRegion存儲的Rowkey範圍
這樣一來,指定範圍內的數據就會被寫入到指定的HRegion中,能夠省略不少IO操做

經過對數據的特性進行分析預先建立分區能夠有效的解決HBase中的數據傾斜問題

以存儲日誌訪問的記錄爲例
根據數據量和每一個HRegion的大小,預先建立空的HRegion,每一個HRegion都設定了其RowKey的範圍,插入數據時直接寫入指定的HRegion中
或者根據數據的訪問特性(28原則,進行抽樣調查,獲得數據頻繁的狀況),將常常訪問的ip斷分割爲幾個HRegion,其餘爲一個或者幾個HRegion
根據具體需求肯定預分區是預估仍是抽樣,預分區的時候儘量的想到各類各樣的狀況

用戶能夠經過HBase提供的工具在建立表的時候指定region的個數:

/bin/hbase org.apache.hadoop.hbase.util.RegionSplitter

關於預拆分region的數量,能夠先按照每一個服務器10個region並隨着時間的推移觀察數據的增加狀況
先設置較少的region在稍後滾動拆分是一種很好的方式,由於一開始就設置不少region的話會影響集羣的性能

執行負載均衡

master中有一個內置的負載均衡器,默認每五分鐘執行一次(經過hbase.balancer.period屬性控制)
其功能是均勻分配region到全部region服務器

其實這個自動的均衡器所起到的效果和以前的手動管理region拆分是同樣的
都是爲了將region的負載平衡到整個集羣中

當用戶想控制某張表特定region的確切位置的時候使用手動管理的方式是比較方便的
咱們能夠經過HBase的balancer命令來顯示的啓動均衡器,或者經過balance_switch開啓和關閉均衡器

合併region

在某些特殊的狀況下,用戶可能須要對服務器上的region進行合併(好比刪除大量數據而且想減小服務器region數量的狀況下?)
HBase集成了一個工具可以讓用戶在集羣沒有工做的時候合併兩個相鄰的region:

/bin/hbase org.apache.hadoop.hbase.util.Merge

參數分別是表名要合併的兩個region名,region的信息能夠在WebUI中獲得,也能夠從scan .META表中得到

客戶端API優化

寫代碼使用HBase客戶端訪問HBase的時候是有很大優化空間的
下面給出一個列表參考:

禁用自動刷寫

當有大量的寫入操做時,使用setAutoFlush(false)關閉自動刷寫特性
這樣一來,數據將會被寫入緩存中達到指定值的時候一塊兒發送到服務器
也能夠經過flushCommits()來強制刷寫
調用HTable的close方法會隱式的調用flushCommits()

使用掃描緩存

若是HBase做爲一個MapReduce做業的而輸入源,最好將MapReduce做業的輸入掃描器實例的緩存用setCaching()設置爲比1大的多的值
例如設置爲500的時候則一次能夠傳送500行數據到客戶端進行處理

限定掃描範圍

若是隻處理少數列,則應當只有這些列被添加到Scan的輸入中
由於若是沒有作篩選,則會掃描其餘的數據存儲文件

關閉ResultScanner

這不會帶來性能提高,可是會避免一些性能問題
因此必定要在try/catch中關閉ResultScanner

關閉Put的WAL

使用Put的writeToWAL(false)關閉WAL能夠提升吞吐量
可是帶來的影響是在服務器故障的時候將會丟失數據
並且關閉日誌所帶來的性能提高也不是很明顯,因此不推薦使用

配置優化

啓動集羣的時候,配置文件中一樣有不少能夠優化的選項

減小Zookeeper超時的發生

默認region服務器和ZK的通訊超時時間是3分鐘
這意味着當服務器出現故障的時候,master會在3分鐘以後發現並進行處理

能夠經過zookeeper.session.timeout將這個值設置爲1分鐘或者其餘狀況
如此一來服務器當掉的時候master能夠儘早發現

可是要注意的是:以前咱們提到過,在垃圾回收的時候JAVA進程將會被中止一段時間
若是這個值過小的話有可能形成ZK的誤判

增長處理線程

默認狀況下HBase響應外部用戶訪問數據表請求的線程數爲10
設置的有點小了,這是爲了防止用戶在客戶端高併發使用較大寫緩衝區的狀況使得服務器過載

咱們能夠經過hbase.regionserver.handler.count屬性將這個值設置爲最大客戶端數目
可是也不宜太大,由於併發的寫請求涉及到的數據累加起來以後可能會對一個region服務器的內存形成壓力

增長堆大小

HBase使用默認一組合理而且保守的配置以知足大多數不一樣機型的測試
若是用戶有更好的服務器則能夠分配給HBase8G甚至更大的空間

能夠經過hbase-env.sh中的HBASE_HEAPSIZE來進行全局的設置
若是隻想更改region服務器的內存,則能夠單獨設置HBASE_REGIONSERVER_OPTS選項,master將會以默認的1G繼續運行

增長region大小

更大的region能夠減小集羣總的region數量,通常來講管理較少的region可讓集羣更加平穩的運行
默認狀況下region的大小是256MB,咱們能夠配置1GB或者更大的region
region的大小也是須要評估的,由於太大的region也覺得這在高負載的狀況下合併的停頓時間更長

更多的配置調優能夠參考《HBase權威指南》中性能優化的配置小節

做者:@小黑