1. 程式人生 > >HBase Rowkey 設計

HBase Rowkey 設計

HBase中的rowkey唯一的決定了一行資料,使用HBase的場景多種多樣, rowkey設計的好壞很大程度上決定了應用場景中的執行效率。

通過具體的一個場景樣例,簡單的研究一下在Hbase的rowkey設計上的一些原則。

樣例場景

假設當前有一個網上商城系統, 需要實現使用者拉取歷史訂單的需求。當然, 在歷史訂單量比較少的時候,簡單的使用mysql就能很好的支援需求,但是當資料量到一定量級,尤其是對於寫多讀少的場景的時候,HBase也會成為我們的一個選擇。

如果要使用HBase來實現使用者歷史訂單的這麼一個需求(據我所知,京東和滴滴的歷史訂單也是用HBase來支撐的),我們該如何設計rowkey呢。

最直觀的, 當然是將使用者的ID直接當作rowkey的一部分。當然,只有使用者的ID是不夠的, 為了表示多個訂單, 一般可以將時間戳作為rowkey的一部分,比如{{user_id}}_{{timestamp}},這樣一個訂單作為一樣插入到HBase中,拉取的時間將使用者的ID當作字首,可以將所有當前使用者的訂單拉取出來。

但是這樣肯定是有問題的, 我們將其中存在的問題一一列出來並給出相應的解決辦法。

熱點問題

第一個首先是熱點問題,一般使用者的ID會存在一定的規律性, 比如自增性。考慮一種場景, 如果商城推出了一項新活動, 吸引了大量新使用者來下單。那麼這波新使用者的使用者ID很大可能上是相似的,這樣的話,大量的單子就會落到HBase中一個或極少數節點。這就是所謂的熱點問題,大量的訪問會使得熱點region所在的機器承受了大部分壓力,引起效能的下降,但是同時其他region有可能是比較閒置的狀態。

那麼如何儘量的避免這種不均衡的狀態呢,當然可選擇的方式也是多樣的。

加鹽

加鹽就是所謂的salt值,通過在rowkey中加入一個隨機值,使得即使使用者ID相近,加鹽之後仍舊能夠分配到不同的region。

雜湊

雜湊也是一個常用的打散資料的方式,比如在我們的例子中, 固定一種雜湊演算法,生成rowkey的時候不是直接用使用者的ID,而是將使用者的ID經過雜湊之後的值當作rowkey的一部分,這樣即使短時間內使用者的ID都是相鄰的,他們得到的雜湊值也是分散的。在拉取的時候,因為雜湊演算法固定,能夠很快的通過使用者的ID找到雜湊之後的值。

翻轉

使用者的ID如果具有自增性, 這個就表示很多使用者的ID其實前面部分是相同的,只是後半部分在一直變化。那麼對使用者的ID進行反轉之後,就是前半部分在一直變化了, 這樣也能達到打散分佈的效果。

訪問順序問題

如果用翻轉的方式解決熱點問題, 現在使用者歷史訂單的rowkey的格式就是{{reverse(user_id)}}_{{timestamp}}。不過這樣也會有問題, 一般檢視歷史訂單的時候,都是要求最近的單子顯示在最前邊, 如果按照這種rowkey的方式去存放資料,由於HBase中的資料都是按照字典序排序的,沒有辦法按照時間的逆序取資料。

翻轉時間戳

所以通常的做法是翻轉時間戳,在上邊的的rowkey例子中, 將timestamp替換成Long.Max_Value - timestamp,即rowkey的格式是{{reverse(user_id)}}_{{Long.Max_Value - timestamp}}。

這麼做有什麼好處呢, 我們可以想見時間越近的單子,Long.Max_Value - timestamp會越小,也就是會排的越前面。 當取歷史單子的時候,我們用Long.Max_Value減去當前時間戳得到的數值甚至比最近單子的字尾還要小,將“Long.Max_Value減去當前時間戳” 當作scan的startRow就能逆序取到最新的所有訂單列表。

當然, 在設計的時候還有其他一些規則,比如col_family要進行短,rowkey也要儘量短等等, 不再贅述。