隨着大數據的越來越普及,HBase也變得越來越流行。使用HBase並不困難,但是如何用好HBase,這確是一個難點。為了合理地使用HBase,盡可能發揮HBase的功能,我們需要根據不同的場景對HBase進行不同地優化以最大程度上提升系統的性能。本文重點介紹列族設計有關的優化。我們先來了解下HBase列族具有哪些屬性配置。
列族屬性配置
版本數量(VERSIONS)
每個列族可以單獨設置行版本數,默認是3。這個設置很重要,因為HBase是不會去覆蓋一個值的,它只會在后面追加寫,用時間戳(版本號)來區分,過早的版本會在執行Major Compaction
時刪除。這個版本的值可以根據具體的場景來增加或減少。
不推薦將版本最大值設置成一個很高的水平,除非老數據對你也非常重要。過多的版本,會導致存儲文件變大,以至於影響查詢效率。
最小版本數(MIN_VERSIONS )
每個列族可以設置最小版本數,最小版本數缺省值是0,表示禁用該特性。最小版本數參數和存活時間是一起使用的,允許配置“如保存最后T秒有價值的數據,最多N個版本,但最少M個版本”(M是最小版本,M<N)。該參數僅在存活時間對列族啟用,且必須小於行版本數。
存活時間(TTL)
HBase支持配置版本數據的存活時間(TTL),TTL設置了一個基於時間戳的臨界值,HBase會自動檢查TTL值是否達到上限,如果TTL達到上限,則該數據會在Major Compaction過程中被刪除。
數據塊大小(BLOCKSIZE )
hbase默認的塊大小是64kb,不同於HDFS默認64MB的塊大小。原因是hbase需要支持隨機訪問,一旦找到了行鍵所在的塊,接下來就會定位對應的單元格。使用64kb的塊掃描的速度顯然優於64MB大小的塊。
對於不同的業務數據,塊大小的合理設置對讀寫性能有很大的影響。如果業務請求以Get請求為主,可以考慮將塊大小設置較小;如果以Scan請求為主,可以將塊大小調大;默認的64K塊大小是在Scan和Get之間取得的一個平衡。
注意:
默認塊大小適用於多種數據使用模式,調整塊大小是比較高級的操作。配置錯誤將對性能產生負面影響。因此建議在調整之后進行測試,根據測試結果決定是否可以線上使用。
塊緩存(BLOCKCACHE)
默認是true。緩存是內存存儲,hbase使用塊緩存將最近使用的塊加載到內存中。塊緩存會根據最近最久未使用(LRU)”的規則刪除數據塊。
如果你的使用場景是經常順序訪問或者很少被訪問,可以關閉列族的緩存。列族緩存默認是打開的。
激進緩存的配置(IN_MEMORY)
HBase可以選擇一個列族賦予更高的優先級緩存,激進緩存(表示優先級更高),IN_MEMORY
默認是false。如果設置為true,hbase會嘗試將整個列族保存在內存中,只有在需要保存是才會持久化寫入磁盤。但是在運行時hbase會嘗試將整張表加載到內存里。
這個參數通常適合較小的列族。
壓縮(COMPRESSION)
HBase在寫入數據塊到HDFS之前會首先對數據進行壓縮,再落盤,從而減少磁盤空間使用量。而在讀數據的時候首先從HDFS中加載出block塊之后進行解壓縮,然后再緩存到BlockCache,最后返回給用戶。
使用壓縮其實就是使用CPU資源換取磁盤空間資源。
HBase支持三種壓縮方式:LZO、Snappy和GZIP。
默認為NONE,不適用壓縮,
壓縮算法 | 壓縮比率 | 壓縮速度 | 解壓速度 |
---|---|---|---|
GZIP | 13.4% | 21 MB/s | 118 MB/s |
LZO | 20.5% | 135 MB/s | 410 MB/s |
Snappy | 22.2% | 172 MB/s | 409 MB/s |
其中:
-
GZIP的壓縮率最高,但是其實CPU密集型的,對CPU的消耗比其他算法要多,壓縮和解壓速度也慢;
-
LZO的壓縮率居中,比GZIP要低一些,但是壓縮和解壓速度明顯要比GZIP快很多,其中解壓速度快的更多;
-
Snappy的壓縮率最低,而壓縮和解壓速度要稍微比LZO要快一些。
綜合來看,Snappy的壓縮率最低,但是編解碼速率最高,對CPU的消耗也最小,目前一般建議使用Snappy。
復制范圍(REPLICATION_SCOPE )
HBase提供了跨級群同步的功能,本地集群的數據更新可以及時同步到其他集群。復制范圍(replication scope)的參數默認為0,表示復制功能處於關閉狀態。
預分區(SPLITS )
在默認情況下,HBase表在剛剛被創建的時候,只有1個分區(Region),當一個Region的大小達到閾值(通過hbase.hregion.max.filesize
參數控制),Region會進行split,分裂成2個Region。但是在進行split的時候,會消耗大量的資源,頻繁的split會對HBase的性能造成巨大的影響。
HBase提供了預分區的功能,用戶可以在創建表的時候對表按照一定的規則提前進行分區。這樣是進行HBase數據讀寫的時候,會按照Region分區情況,在集群內做數據的負載均衡。
常用分區方法:
create'table','cf', SPLITS => ['1', '2', '3', '4', '5', '6', '7', '8', '9']
create'table','cf', { NUMREGIONS => 8 , SPLITALGO => 'UniformSplit' }
create'table','cf', { NUMREGIONS => 10, SPLITALGO => 'HexStringSplit' }
BLOOMFILTER
BloomFilter主要用來過濾不存在待檢索RowKey或者Row-Col的HFile文件,避免無用的IO操作。它會告訴你在這個HFile文件中是否可能存在待檢索的KV,如果不存在,就可以不用消耗IO打開文件進行seek。通過設置BloomFilter可以提升隨機讀寫的性能。
BloomFilter是一個列族級別的配置屬性,如果在表中設置了BloomFilter,那么HBase會在生成StoreFile時包含一份BloomFilter結構的數據,稱其為MetaBlock
和DataBlock
(真實KeyValue數據)一起由LRUBlockCache維護。所以開啟BloomFilter會有一定的存儲即內存Cache的開銷。
BloomFilter取值有兩個,row
和rowcol
,需要根據業務來確定具體使用哪種。
-
如果業務大多數隨機查詢時僅僅使用row作為查詢條件,BloomFilter一定要設置為row;
-
如果大多數隨機查詢使用row+cf作為查詢條件,BloomFilter需要設置為rowcol;
-
如果不確定查詢類型,建議設置為row。
列族設置
列族數量
不要在一張表中定義太多的列族。目前HBase並不能很好的處理2~3以上的列族,flush
和compaction
操作是針對一個Region的。
當一個列族操作大量數據的時候會引發一個flush,它鄰近的列族也會因關聯效應被觸發flush,盡管它沒有操作多少數據。compaction操作是根據一個列族下的全部文件的數量觸發的,而不是根據文件大小觸發的。
當很多的列族在flush和compaction時,會造成很多沒用的IO負載。
盡量在模式中只針對一個列族進行操作。將使用率相近的列歸為一個列族,這樣每次訪問就只用訪問一個列族,既能提升查詢效率,也能保持盡可能少的訪問不同的磁盤文件。
列族的基數
如果一個表存在多個列族,要注意列族之間基數(如行數)相差不要太大。例如列族A有100萬行,列族B有10億行,按照RowKey切分后,列族A可能被分散到很多很多Region(及RegionServer),這導致掃描列族A十分低效。
列族名、列名長度
列族名和列名越短越好,冗長的名字雖然可讀性好,但是更短的名字在HBase中更好。
一個具體的值由存儲該值的行鍵、對應的列(列族:列
)以及該值的時間戳決定。HBase中索引是為了加速隨機訪問的速度,索引的創建是基於“行鍵+列族:列+時間戳+值
”的,如果行鍵和列族的大小過大,甚至超過值本身的大小,那么將會增加索引的大小。並且在HBase中數據記錄往往非常之多,重復的行鍵、列將不但使索引的大小過大,也將加重系統的負擔
總結
根據HBase列族的這些屬性配置,結合我們的使用場景,HBase列族可以進行如下優化:
-
列族不宜過多,將相關性很強的key-value都放在同一個列族下,;
-
盡量最小化行鍵和列族的大小;
-
提前預估數據量,再根據Rowkey規則,提前規划好Region分區,在創建表的時候進行預分區;
-
在業務上沒有特別要求的情況下,只使用一個版本,即最大版本和最小版本一樣,均為1;
-
根據業務需求合理設置好失效時間(存儲的時間越短越好);
-
根據查詢條件,設置合理的BloomFilter配置;
-
合理設計RowKey,可以參考《一篇文章帶你快速搞懂HBase RowKey設計》。