HBase 之Rowkey設計

Rowkey的作用

  1. Rowkey用於標識唯一的行
  2. HBase中的數據都是根據Rowkey的字典序存儲的,比如memstore中的數據和HFile中的數據
  3. 讀寫數據都需要通過Rowkey來定位Region

Rowkey的設計原則

長度原則

rowkey可以是任意字符串,最大長度64Kb,實際應用中一般爲10-100bytes,以byte[]形式保存,一般設計成定長。且越短越好,一般不要超過16個字節,原因如下:

  1. 數據的持久化文件HFile中是按照KeyValue存儲的,如果rowkey過長,比如超過100字節,1000w行數據,光rowkey就要佔用100*1000w=10億個字節,將近1G數據,這樣會極大影響HFile的存儲效率;
  2. MemStore將緩存部分數據到內存,如果rowkey字段過長,內存的有效利用率就會降低,系統不能緩存更多的數據,這樣會降低檢索效率。
  3. 目前操作系統都是64位系統,內存8字節對齊,控制在16個字節,8字節的整數倍利用了操作系統的最佳特性。
唯一原則

保證RowKey的唯一性,若向HBase中同一張表插入相同RowKey的數據,則原先存在的數據會被新的數據覆蓋

排序原則

RowKey是按照字典序排序的

散列原則

設計的RowKey應均勻的分佈在各個HBase節點上

如何避免熱點

一、加鹽(隨機數)

並不等同於密碼學中的加鹽

具體就是給rowkey分配一個隨機前綴以使得它和之前的rowkey的開頭不同。分配的前綴種類數量應該和你想使用數據分散到不同的region的數量一致。加鹽之後的rowkey就會根據隨機生成的前綴分散到各個region上,以避免熱點。

優點: 可以提高吞吐量,打散之後不同的前綴分配到不同的region。
缺點: 添加隨機數之後無法根據原來的Rowkey查詢,需要到多個region中查找,增加了讀的開銷;同一個Rowkey,但是兩次加隨機數生成的新的Rowkey不一樣;可能存在開始和結尾的區域沒有值的情況,但是也要佔用內存;

查詢數據的時候,如果知道Rowkey,可以使用get精準快速的得到數據,反之,只能使用scan進行模糊匹配查詢,是非常慢的,這裏也可以衍生出索引的概念,進而我們可以使用Phoenix創建鹽表 + 二級索引,增加讀的性能;

hbase 語法創建鹽表:
create 'tablename','CF' SPLIT['a','b','c','d','e']
// 會產生6個region,每個region的Rowkey範圍左閉又開 eg: [,a) 、 [a,b)


Phoenix:
CREATE TABLE SALT_TEST (a_key VARCHAR PRIMARY KEY, a_col VARCHAR) SALT_BUCKETS = 3;
// SALT_BUCKETS取值爲1-256,表示預分區的個數
二、hash
create 'hash_table', 'order', {NUMREGIONS => 10, SPLITALGO => 'HexStringSplit'}

使用如上語法在HBase中創建一張10個分區的hash表,需要注意的是:put數據的時候,並不會自動對Rowkey進行hash,需要自己事先計算組裝好新的Rowkey。一般取hash(Rowkey)的前幾位拼接上Rowkey作爲新的Rowkey。因爲Rowkey太長也會增加維護開銷。

優點: 每一個Rowkey的hash之後的值都是一樣的,不會像鹽表一樣,將同一個Rowkey放到不同的region中。但是同樣可以實現負載均衡。
缺點: 和鹽表一樣,將原來連續的數據打散了,增加了讀的開銷

三、反轉Rowkey

原生數據rk在尾部出現良好的隨機數,比如手機號碼等,就可以考慮翻轉使得隨機分佈,提高吞吐, 但是也打破了數據的自然順序

案例

現有一張用戶訂單表,需要根據用戶查詢最新的訂單記錄
userid  orderno  skuname skuprice skunum skusum      ordercretime
user1  0001      西瓜1    10       5      50        2019-07-07 12:00:00
user2  0002      西瓜2    10       50     500       2019-07-08 12:00:00

需求一 根據用戶查詢訂單最新記錄
where userid=user1 order by ordercretime desc limit 1
需求二 根據用戶查詢某個時間段xxx,xxx的訂單記錄
where userid=user1 and (ordercretime>=‘xxx’ and ordercretime<=‘xxxx’)
需求三 根據時間段查詢訂單記錄
where (ordercretime>=‘xxx’ and ordercretime<=‘xxxx’)
需求四 根據用戶買了西瓜的訂單記錄
where userid=user1 and skuname=‘西瓜’

在一個region中,越新put的數據在越後面。這樣,如果不考慮rowkey,那麼我們只能使用scan來掃描到最新的數據,這是非常慢的且開銷很大。那麼如何來設計Rowkey?
我們取一個足夠大的時間,比如設計的系統準備運行的時間(系統的生命週期)。假設我們設計的系統準備用十年,那麼我們取十年後的時間戳來減去當前的時間戳作爲我們的Rowkey,這樣越新put的數據存儲就會越靠前,這裏需要注意的是將獲得的差值位數用零補齊。eg:

最大的時間戳是:1000
當前時間戳是:565
差值爲:435
那麼我們需要將其補齊爲:0435

Rowkey設計爲

RowKey=hash(userid).substring(0, 4)+userid+ (Long.Max_Value - timestamp)

注意長度補齊

總結

  1. 建立預分區時,需結合自己的需求劃分分區,否則分區不當,產生很多空的region,也會造成資源的浪費;一個region需要佔用hbase.hregion.memstore.mslab.chunksize(2M)大小的memstore空間,可以根據小表1M、中表5M、大表20M來設置。
  2. 一個regionserver上的region數量一般在100-200個。
  3. Rowkey的設計,不可能十全十美,一般取決於自己的最大需求。
  4. 越重要越高頻的字段在Rowkey越靠左邊。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章