面試,HBase如何設計rowkey

2021-10-25 10:46:39 字數 2870 閱讀 6461

hbase中的rowkey是按字典順序排序的,通過rowkey查詢可以對千萬級的資料實現毫秒級響應。然而,如果rowkey設計不合理的話經常會出現乙個很普遍的問題----熱點。當大量client的請求(讀或者寫)只指向集群的乙個節點,或者很少量的幾個節點時,也就代表產生了熱點問題。

避免產生熱點的方式也就是盡可能的將rowkey均勻分散到所有的region上,下面介紹了幾種rowkey設計常用的方式:

加鹽是指在rowkey的字首新增隨機資料,使rowkey盡可能的分布到其他regionserver上

假設遇到下面的rowkey,表的預分割槽設定為每個字母對應乙個region。字首「a」是乙個region,字首「b」是另乙個region等等。那麼在這個表中,所有以「f」開頭的rowkey都將位於同乙個region。比如:

foo0001

foo0002

foo0003

foo0004

那麼,如果你想把它們分散到四個不同的region,那麼就可以使用四種不同的字首: a、b、c和d來做加鹽。在加鹽之後,rowkey也就變成了下面這樣。

a-foo0003

b-foo0001

c-foo0004

d-foo0002

(ps:由於現在可以向四個region寫資料,理論上,效能比之前向同乙個region寫吞吐量提公升四倍)

並且,如果後續有新的資料寫入,rowkey也就會隨機的新增字首,寫到不同的region中

缺點:加鹽雖然可以很大程度的避免熱點問題,提公升寫入效率,但是由於rowkey被隨機的新增了salt值,在讀取時候要付出額外的開銷。具體怎麼讀取加鹽後的資料,後面再做介紹

雜湊的演算法有多種,在rowkey設計中用的比較多的大概就是md5了吧,但是需要注意的是md5雜湊還是有碰撞的可能性的,概率很小,但是不是零。

所以一般使用md5做rowkey雜湊時候,都會附加乙個唯一字段,比如賬號欄位account,對account做md5,擷取6位左右的md5返回值然後再拼接account欄位,也就是:

substr(md5(account))+account
此外,通過md5雜湊之後的rowkey,在建立表預分割槽時候,可以使用hbase自帶的hexstringsplit方法

如果定義的rowkey欄位,前部分資料變化幅度很小,變化很慢,尾部資料變化頻率較高,便可以考慮反轉字段,尤其對類似時間戳的資料

不管以哪種方式設計rowkey,在查詢時候也要做對應的資料處理,比如做hash的,查詢時候也需要先把資料hash之後,然後查詢rowkey;通過反轉方式設計的rowkey同理。

rowkey可以是任意的字串,最大長度64kb,但是建議在設計rowkey時候,盡可能的短,原因:

1.hbase資料儲存是以key-value的形式儲存的,如果rowkey比較長,比如100位元組,那麼1000w行資料,光rowkey儲存就需要100*1000w=10億個位元組,將近1g的資料。

2.memstore的會快取資料到記憶體,如果rowkey比較長,同樣會占用更多的空間

3.建議rowkey設計在8位元組的整數倍,控制在16個位元組,因為目前的作業系統大多都是64位的,整數倍更好了利用了作業系統的特性。

列簇(columnfamily)同理,盡可能的短,最好是乙個字元,比如 f 或者 d

我們知道,long型別是8個位元組,並且你可以通過long型別儲存乙個最大為18,446,744,073,709,551,615的無符號數字,僅僅用8個位元組,但是如果以string型別的形式儲存這樣的數字,那麼幾乎需要3倍空間的大小(假定每個字元佔乙個位元組)

舉個例子驗證一下:

// long

//long l = 1234567890l;

byte lb = bytes.tobytes(l);

system.out.println("long bytes length: " + lb.length);   // returns 8

string s = string.valueof(l);

byte sb = bytes.tobytes(s);

system.out.println("long as string length: " + sb.length);    // returns 10

// hash

//messagedigest md = messagedigest.getinstance("md5");

byte digest = md.digest(bytes.tobytes(s));

system.out.println("md5 digest bytes length: " + digest.length);    // returns 16

string sdigest = new string(digest);

byte sbdigest = bytes.tobytes(sdigest);

system.out.println("md5 digest as string length: " + sbdigest.length);    // returns 26

但是,也有乙個缺點,就是如果使用這種二進位制表示的型別時候,在hbase shell介面查資料的時候,可讀性比較差,比如:

hbase(main):002:0> get 'table1', 'rowkey1'

column                                        cell

f:q                                          timestamp=1369163040570, value=\x00\x00\x00\x00\x00\x00\x00\x01

1 row(s) in 0.0310 seconds

HBase面試須知

表按照行鍵進行了排序,所以查詢時可以很快定位 資料按照行鍵切分為多個hregion,分布在多個regionserver中,查詢大量資料時,多個regionserver可以一起工作,從而提高速度 hregion是存活在regionserver的記憶體中的,讀寫會非常的高效 還有hfile的支援保證大量...

面試 Hbase面試問題

1.hbase怎麼預分割槽?2.hbase怎麼給web前台提供介面來訪問?3.htable api有沒有執行緒安全問題,在程式中是單例還是多例?4.hbase有沒有併發問題?5.metaq訊息佇列,zookeeper集群,storm集群,就可以完成對 推薦系統功能嗎?還有沒有其他的中介軟體?6.st...

Hbase 列族設計

在大多數的工廠環境下,往往只會設計乙個列族,因為列族數量過多會導致如下的效能問題 1.flush 會產生大量 io flush 的最小單元是 region,也就是說乙個 region 中的某個列族做 flush 操作,其他的 列族也會 flush,對每個列族而言,每次 flush 都會產生乙個檔案,...