HBase 熱點問題——rowkey散列和預分區設計

熱點發生在大量的client直接訪問集羣的一個或極少數個節點(訪問多是讀,寫或者其餘操做)。大量訪問會使熱點region所在的單個機器超出自身承受能力,引發性能降低甚至region不可用,這也會影響同一個RegionServer上的其餘region,因爲主機沒法服務其餘region的請求,形成資源浪費。設計良好的數據訪問模式以使集羣被充分,均衡的利用。 
數據傾斜:Hbase能夠被劃分爲多個Region,可是默認建立時只有一個Region分佈在集羣的一個節點上,數據一開始時都集中在這個Region,也就是集中在這一個節點上,就算region存儲達到臨界值時被劃分,數據也是存儲在少數節點上。這就是數據傾斜node

隨機散列與預分區兩者結合起來,是比較完美的。預分區一開始就預建好了一部分region,這些region都維護着本身的start-end keys,在配合上隨機散列,寫數據能均衡的命中這些預建的region,就能解決上面的那些缺點,大大提供性能。算法

1. 預分區

1.1 HBase的預分區概述

默認分區:shell

HBase表被建立時,只有1個Region,當一個Region過大達到默認的閥值時(默認10GB大小),HBase中該Region將會進行split,分裂爲2個Region,以此類推。工具

缺點:性能

表在進行split的時候,會耗費大量的資源,頻繁的分區對HBase的性能有巨大的影響。因此,HBase提供了預分區功能,即用戶能夠在建立表的時候對錶按照必定的規則分區。編碼

2. HBase預分區的做用

避免HBase常常split,產生沒必要要的資源消耗,提升HBase的性能spa

3. HBase預分區的方法

  • HBase Shell
create 'user1',{NAME=>'f'},{NAME=>'d'},SPLITS=>['0|','1|','3|','4|']
create 'user1', 'f', SPLITS => ['1|', '2|', '3|', '4|']
  • HBase Shell(經過讀取split文件)
create 'user2',{NAME=>'f'},{NAME=>'d'},SPLITS_FILE=>'/data/hbaseSplit.txt'
hbaseSplit.txt內容
> cat hbaseSplit.txt
1|
2|
3|
4|
  • HBase Java API
object HbaseUtil { def main(args: Array[String]): Unit = { 
    val conf = HBaseConfiguration.create() 
conf.set("hbase.zookeeper.quorum","192.168.1.11,192.168.1.12,192.168.1.13") conf.set("hbase.zookeeper.property.clientPort", "2181") conf.set("zookeeper.znode.parent", "/hbase") conf.set("hbase.master", "192.168.1.11:16010") val connection = ConnectionFactory.createConnection(conf) val admin = connection.getAdmin val colFamily = List("info", "desc") val tableName = "user3" val splitKeys = Array( Bytes.toBytes("0|"), Bytes.toBytes("1|"), Bytes.toBytes("2|"), Bytes.toBytes("3|"), Bytes.toBytes("4|") ) if (admin.tableExists(TableName.valueOf(tableName))) { println("表已存在!") } else { val descriptor = new HTableDescriptor(TableName.valueOf(tableName)) colFamily.foreach(x => descriptor.addFamily(new HColumnDescriptor(x))) admin.createTable(descriptor, splitKeys) } admin.close() connection.close() } }

2.隨機散列

1. 固定散列值線程

  • HBase Shell
create 'user1',{NAME=>'f'},{NAME=>'d'},SPLITS=>['0|','1|','3|','4|','5|','6|','7|','8|','9|']

說明:固定值散列,後面添加"|",由於|編碼值最大設計

2. 哈希(散列)code

Hbase自帶了兩種pre-split的算法,分別是 HexStringSplit 和  UniformSplit 。

  • HexStringSplit 

若是咱們的row key是十六進制的字符串做爲前綴的,稱之爲HexStringSplit就比較適合用HexStringSplit,做爲pre-split的算法。例如,咱們使用HexHash(prefix)做爲row key的前綴,其中Hexhash爲最終獲得十六進制字符串的hash算法,咱們一般手動指定SPLITS來指定預分區,咱們也能夠用咱們本身的split算法。

create 'test',{NAME=>'f',COMPRESSION=>'SNAPPY'},{NUMREGIONS => 30, SPLITALGO => 'HexStringSplit'}

 

put時可使用

MD5Hash.getMD5AsHex(Bytes.toBytes(str));
  • UniformSplit

若是咱們的row key使用byte來做爲前綴,稱之爲UniformSplit,若是某個hbase的表查詢只是以隨機查詢爲主,能夠用UniformSplit的方式進行,它是按照原始byte值(從0x00~0xFF)右邊以00填充。以這種方式分區的表在插入的時候須要對rowkey進行一個技巧性的改造, 好比原來的rowkey爲rawStr,則須要對其取hashCode,而後進行按照比特位反轉後放在最初rowkey串的前面。能夠充分利用Bytes這個工具類來作。

create 'test', { NAME => 'f', TTL => 5184000, DATA_BLOCK_ENCODING => 'PREFIX' }, {NUMREGIONS => 128, SPLITALGO => 'UniformSplit'}

使用SPLITALGO => 'UniformSplit'方式來建表是沒有指定startKey和endKey的,也就是說採用這種方式建表就是基於ancil的256個值的範圍來平均切分10個預分區的(因爲anscil一共256個值,所以採用這種方式作預分區建表最多支持256個預分區,不過在寫入數據後,256預分區能夠再內部作二次切分),採用這種作法致使在Scan查詢的時候就須要開256個Scan線程取掃描數據並返回最終的結果,好處就是統一了整個rowkey的範圍,取名UniformSplit大概也是這個意思。

put數據時,能夠充分利用Bytes這個工具類

byte[] rowKey = Bytes.add(Bytes.toBytes(Integer.reverse(Integer.valueOf(Integer.valueOf(i).hashCode()))), Bytes.toBytes(i));

3.強制split

HBase 容許客戶端強制執行split,在hbase shell中執行如下命令:

 split 'forced_table', 'b' 

其中:forced_table 爲要split的table , ‘b’ 爲split 點

相關文章
相關標籤/搜索