由於 HBase 運行在 JVM,JVM 的 Garbage Collection(GC) 設置對於 HBase 流暢的運行,更高的性能是很是重要的,除了配置 HBase 堆設置的指導方針以外。有 HBase 進程輸出到它們的 GC 日誌中是一樣重要的,而且它們基於 GC 日誌的輸出調整 JVM 設置。php
我將描述最重要的 HBase JVM 堆設置,也描述怎樣是它生效以及理解 GC 日誌,在這方面。我將覆蓋一些指導方針來調整 HBase 的 Java GC 設置。java
登錄你的 HBase region 服務器。nginx
如下被建議用於 Java GC 和 HBase 堆設置:git
經過編輯 hbase-env.sh 文件給 HBase 足夠大的堆大小。好比,如下片斷給 HBase 配置一個 8000-MB 的堆:github
$ vi $HBASE_HOME/conf/hbase-env.shexport HBASE_HEAPSIZE=8000
經過如下命令使得 GC 日誌生效:算法
export HBASE_OPTS="$HBASE_OPTS -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/local/hbase/logs/gc-hbase.log"
把如下代碼加入來比默認更早的開始 Concurrent-Mark-Sweep GC(CMS):shell
$ vi $HBASE_HOME/conf/hbase-env.shexport HBASE_OPTS="$HBASE_OPTS -XX:CMSInitiatingOccupancyFraction=60"
在集羣中同步變動並重啓 HBase。apache
檢查輸出到指定日誌文件中(/usr/local/hbase/logs/gc-hbase.log)的 GC 日誌。GC 日誌看起來像如下屏幕截圖:ruby
在步驟 1 中,咱們配置 HBase 堆內存大小。默認,HBase 使用 1GB 的堆,這對於現代的機器來講過低了。對於 HBase 來講,比 4GB 更大是好的。咱們建議 8GB 或更大,可是低於 16 GB。bash
在步驟 2 中,咱們是 JVM 日誌生效,使用這個設置,你能夠獲取 region 服務器的 JVM 日誌,和咱們在步驟 5 中展現的相似。關於 JVM 內存分配和垃圾回收的基礎知識是被要求的,爲了明白日誌輸出。如下是 JVM 分代垃圾收集系統的圖表:
這裏有 3 個堆分代:Perm(或是 Permanent)代【永久代】,Old Generation 代【老年代】,和 Young 代【年輕代】。年輕代由三個獨立的空間組成,Eden 空間和兩個 survivor 空間,S0 和 S1。
一般,對象被分配在年輕代的 Eden 空間,若是一個分配失敗(Eden 滿了),全部 java 線程中止,而且一個年輕代 GC(Minor GC)被調用。全部在年輕代存活的對象(Eden 和 S0 空間)被拷貝到 S1 空間。若是 S1 空間滿了,對象被拷貝(提高)到老年代。當一個提高失敗,老年代被收集(Major/Full GC)。永久代和老年代一般一塊兒被收集。永久代被用於在存放類和對象中定義的方法。
回到咱們示例的步驟 5,上述選項產出的 minor GC 輸出爲如下形式:
<timestamp>: [GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs] [Times: <user time> <system time>, <real time>]
在這個輸出中:
timestamp 是 GC 發生的時間,相對於應用的啓動時間。
collector 是 collector 用於 minor collection 的內部名字
starting occupancy1 是年輕代在垃圾回收前的佔用
ending occupancy1 是年輕代在垃圾回收後的佔用
pause time1 是 minor collection 中斷的時間
starting occupancy3 是在垃圾回收前整個堆的佔用
ending occupancy3 是在垃圾回收後整個堆的佔用
pause time3 是整個垃圾回收的中斷時間,這包括 major collection。
[Time:] 解釋了花費在垃圾收集的時間,用戶時間,系統時間,實際時間。
在步驟 5 中咱們輸出的第一行代表了是一個 minor GC,中斷了 JVM 0.0764200 秒,它已經把年輕代的空間從 14.8MB 下降到 1.6MB。
接着,咱們看看 CMS GC 日誌,HBase 使用 CMS GC 做爲它默認的老年代垃圾回收器。
CMS GC 執行如下步驟:
初始化標記
併發標記
重複標記
併發休眠
CMS 僅僅在它初始化標記和重複標記的階段中斷應用進程。在併發標記和睡眠階段,CMS 線程隨着應用線程一塊兒運行。
在該示例的第二行代表了 CMS 初始化標記花費了 0.0100050 秒,併發標記花費了 6.496 秒。注意,併發標記,Java 不會被中斷。
在 GC 日誌的早期屏幕截圖中,在行開始於 1441.435: [GC[YG occupancy:…] 的地方有一箇中斷。這裏的中斷是 0.0413960 秒,用於重複標記堆。以後,你能夠看到睡眠開始了。CMS 睡眠花費了 3.446 秒,可是堆大小在這裏沒有變化太多(它繼續佔據大約 150MB)。
這裏的調整點是使得全部的中斷時間更低。爲了保持中斷時間更低,你須要使用 -XX:NewSize 和 -XX:MaxNewSize JVM 參數調全年輕代空間大小,爲了將它們設置爲相對較小的值(好比,調高几百 MB)。若是服務器有更多的 CPU 資源,咱們建議經過設置 -XX:+UseParNewGC 選項使用 Parallel New Collector。你或許也想爲你的年輕代調整 parallel GC 線程數量,經過 -XX:ParallelGCThreads JVM 參數。
咱們建議加入上述設置到 HBASE_REGIONSERVER_OPTS 變量中,代替 hbase-env.sh 文件中的 HBASE_OPTS 變量。HBASE_REGIONSERVER_OPTS 僅僅影響 region 服務器的進程,這很是好,由於 HBase master 既不處理重型任務也不參與數據處理。
對於老年代來講, concurrent collection (CMS) 一般不能被加速,可是它能夠更早的開始。當分配在老年代的空間比率超過了一個閥值,CMS 開始運行。這個閥值是被收集器自動計算的。對於有些狀況,特別是在加載期間,若是 CMS 開始的太晚,HBase 或許會直接進行 full garbage collection。爲了不這個,咱們建議設置 -XX:CMSInitiatingOccupancyFraction JVM 參數來精確指定在多少百分比 CMS 應該被開始,正如咱們在步驟 3 中作的那樣。在 百分之 60 或 70 開始是一個好的實踐。當老年代使用 CMS,默認的年輕代 GC 將被設置成 Parallel New Collector。
若是你以前使用的是 HBase 0.92 版本,考慮使用 MemStore-Local 分配 Buffer 來預防老年代堆碎片,在頻繁寫的負載下:
$ vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.hregion.memstore.mslab.enabled</name> <value>true</value> </property>
這個特性在 HBase 0.92 中是默認開啓的。
HBase 另一個最重要的特性就是使用壓縮。它是很是重要的,由於:
壓縮下降從 HDFS 讀寫的字節數
節約磁盤空間
當從一個遠程服務器獲取數據的時候,提高了網絡帶寬的效率
HBase 支持 GZip 和 LZO 格式,個人建議是使用 LZO 壓縮算法,由於它解壓數據快而且 CPU 使用率低。更好的壓縮比是系統的首選,你應該考慮 GZip。
不幸的是,HBase 不能使用 LZO,由於 license 問題。HBase 是 Apache-licensed,然而 LZO 是 GPL-licensed。所以,咱們須要本身安裝 LZO。咱們將使用 hadoop-lzo 庫,給 Hadoop 帶來了變形的 LZO 算法。
在這方面,咱們將描述怎樣安裝 LZO 和怎樣配置 HBase 使用 LZO 壓縮。
確保在 hadoop-lzo 被構建的機器上 Java 安裝了。Apache Ant 被要求用來從源碼構建 hadoop-lzo。經過運行一下命令來安裝 Ant:
$ sudo apt-get -y install ant
集羣中的全部節點須要有原生的 LZO 庫被安裝。你能夠經過使用如下命令安裝:
$ sudo apt-get -y install liblzo2-dev
咱們將使用 hadoop-lzo 庫來給 HBase 添加 LZO 壓縮支持:
從 https://github.com/toddlipcon/hadoop-lzo 獲取最新的 hadoop-lzo 源碼
從源碼構建原生的 hadoop-lzo 庫。依賴於你的 OS,你應該選擇構建 32-bit 或 64-bit 的二進制包。好比,爲了構建 32-bit 二進制包,運行如下命令:
$ export JAVA_HOME="/usr/local/jdk1.6"$ export CFLAGS="-m32"$ export CXXFLAGS="-m32"$ cd hadoop-lzo$ ant compile-native$ ant jar
這些命令將建立 hadoop-lzo/build/native 目錄和 hadoop-lzo/build/hadoop-lzo-x.y.z.jar 文件。爲了構建 64-bit 二進制包,你須要改變 CFLAGS 和 CXXFLAGS 成 m64。
拷貝構建的包到你master 節點的 $HBASE_HOME/lib 和 $HBASE_HOME/lib/native 目錄:
hadoop@master1$ cp hadoop-lzo/build/hadoop-lzo-x.y.z.jar $HBASE_HOME/lib hadoop@master1$ mkdir $HBASE_HOME/lib/native/Linux-i386-32hadoop@master1$ cp hadoop-lzo/build/native/Linux-i386-32/lib/* $HBASE_HOME/lib/native/Linux-i386-32/
對於一個 64-bit OS,把 Linux-i386-32 改變成(在前面步驟中) Linux-amd64-64。
添加 hbase.regionserver.codecs 的配置到你的 hbase-site.xml 文件:
hadoop@master1$ vi $HBASE_HOME/conf/hbase-site.xml<property><name>hbase.regionserver.codecs</name><value>lzo,gz</value></property>
在集羣中同步 $HBASE_HOME/conf 和 $HBASE_HOME/lib 目錄。
HBase ships 使用一個工具來測試壓縮是否被正確設置了。使用這個工具來在集羣中的每一個節點上測試 LZO 設置。若是一切都正確無誤的配置了,你將獲得成功的輸出:
hadoop@client1$ $HBASE_HOME/bin/hbase org.apache.hadoop.hbase.util.CompressionTest /tmp/lzotest lzo12/03/11 11:01:08 INFO hfile.CacheConfig: Allocating LruBlockCache with maximum size 249.6m12/03/11 11:01:08 INFO lzo.GPLNativeCodeLoader: Loaded native gpl library12/03/11 11:01:08 INFO lzo.LzoCodec: Successfully loaded & initialized native-lzo library [hadoop-lzo rev Unknown build revision]12/03/11 11:01:08 INFO compress.CodecPool: Got brand-new compressor12/03/11 11:01:18 INFO compress.CodecPool: Got brand-new decompressor SUCCESS
經過使用 LZO 壓縮建立一個表來測試配置,並在 HBase Shell 中驗證它:
$ hbase> create 't1', {NAME => 'cf1', COMPRESSION => 'LZO'} $ hbase> describe 't1'DESCRIPTION ENABLED {NAME => 't1', FAMILIES => [{NAME => 'cf1', BLOOMFILTER => 'NONE', true REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'LZO', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN _MEMORY => 'false', BLOCKCACHE => 'true'}]} 1 row(s) in 0.0790 seconds
hbase.hregion.majorcompaction 屬性指定了在 region 上全部存儲文件之間的 major compactions 時間。默認是時間是 86400000,即一天。咱們在步驟 1 中把它設置爲 0,是禁止自動的 major compaction。這將預防 major compaction 在繁忙加載時間運行,好比當 MapReduce 任務正運行在 HBase 集羣上。
換句話說, major compaction 被要求來幫助提高性能。在步驟 4 中,咱們已經展現了經過 HBase Shell 怎樣在一個特別的 region 上手動觸發 major compaction 的示例。在這個示例中,咱們已經傳遞了一個 region 名字給 major_compact 命令來僅僅在一臺單獨的 region 上調用 major compaction。它也可能在一張表中的全部 region 上運行 major compaction,經過傳遞表名給該命令。major_compact 命令爲 major compaction 給指定的表或 region 排隊;可是經過 region 服務器託管它們,這些將在後臺執行。
正如咱們在早前提到的,你或許僅僅想在一個低負載時期手動執行 major compaction。這能夠很容易的經過一個定時任務調用 major_compact 來實現。
另一個調用 major compaction 的方法就是使用 org.apache.hadoop.hbase.client.HBaseAdmin 類提供的 majorCompact API。在 Java 中很是容易調用這個 API。所以你能夠從 Java 中管理複雜的 major compaction 調度。
一般一個 HBase 表從一個單獨的 region 開始。儘管如此,由於數據保持增加和 region 達到了它配置的最大值,它自動分紅兩份,以致於它們能處理更多的數據。如下圖表展現了一個 HBase region 拆分:
這是 HBase region 拆分的默認行爲。這個原理在大多數狀況下工做的很好,然而有遇到問題的狀況,好比 split/ compaction 風暴問題。
隨着統一的數據分佈和增加,最後在表中的全部 region 都須要在同一時間拆分。緊接着一個拆分,壓縮將在子 region 運行以重寫他們的數據到獨立的文件中。這會引發大量的磁盤 I/O 讀寫和網絡流量。
爲了不這樣的狀況,你能夠關閉自動拆分和手動調用它。由於你能夠控制在什麼時候調用拆分,它能夠幫助擴展 I/O 負載。另外一個優點是,手動拆分可讓你有更好的 regions 控制,幫助你跟蹤和解決 region 相關的問題。
在這方面,我將描述怎樣關閉自動 region 拆分和手動調用它。
使用你啓動集羣的用戶登陸進你的 HBase master 服務器。
爲了關閉自動 region 拆分和手動調用它,遵循如下步驟:
在 hbase-site.xml 文件中加入如下代碼:
$ vi $HBASE_HOME/conf/hbase-site.xml<property><name>hbase.hregion.max.filesize</name><value>107374182400</value></property>
在集羣中同步這些變動並重啓 HBase。
使用上述設置,region 拆分將不會發生直到 region 的大小到達了配置的 100GB 閥值。你將須要在選擇的 region 上明確調用它。
爲了經過 HBase Shell 運行一個 region 拆分,使用如下命令:
$ echo "split 'hly_temp,,1327118470453.5ef67f6d2a792fb0bd737863dc00b6a7.'" | $HBASE_HOME/bin/hbase shell HBase Shell; enter 'help<RETURN>' for list of supported commands. Type "exit<RETURN>" to leave the HBase Shell Version 0.92.0, r1231986, Tue Jan 17 02:30:24 UTC 2012split 'hly_temp,,1327118470453.5ef67f6d2a792fb0bd737863dc00b6a7.'0 row(s) in 1.6810 seconds
hbase.hregion.max.filesize 屬性指定了最大的 region 大小(bytes)。默認,值是 1GB( HBase 0.92 以前的版本是 256MB)。這意味着當一個 region 超過這個大小,它將拆分紅兩個。在步驟 1 中咱們設置 region 最大值爲 100GB,這是一個很是高的數字。
由於拆分不會發生直到超過了 100GB 的邊界,咱們須要明確的調用它。在步驟 4,咱們在一個指定的 region 上使用 split 命令經過 HBase Shell 調用拆分。
不要忘記拆分大的 region。一個 region 在 HBase 是基礎的數據分佈和負載單元。Region 應該在低負載時期被拆分紅合適的大小。
換句話說;太多的拆分很差,在一臺 region 服務器上有太多的拆分會下降它的性能。
在手動拆分 region 以後,你或許想觸發 major compaction 和負載均衡。
咱們在前面的設置會引發整個集羣有一個默認的 100GB 的region 最大值。除了改變整個集羣,當在建立一張表的時候,也能夠在一個列簇的基礎上指定 MAX_FILESIZE 屬性。
$ hbase> create 't1', {NAME => 'cf1', MAX_FILESIZE => '107374182400'}
像 major compaction,你也可使用 org.apache.hadoop.hbase.client.HBaseAdmin 類提供的 split API。