大白話徹底搞懂HBase RowKey詳細設計

寫在前面:我是「且聽風吟」,目前是某上市遊戲公司的大數據開發工程師,熱愛大數據開源技術,喜歡分享自己的所學所悟,現階段正在從頭梳理大數據體系的知識,以後將會把時間重點放在Spark和Flink上面。

如果你也對大數據感興趣,希望在這個行業一展拳腳。歡迎關注我,我們一起努力,一起學習。博客地址:https://ropledata.blog.csdn.net

博客的名字來源於:且聽風吟,靜待花開。也符合我對技術的看法,想要真正掌握一門技術就需要厚積薄發的毅力,同時保持樂觀的心態。

你只管努力,剩下的交給時間!

圖片

一、前言

RowKey作爲HBase的核心知識點,RowKey設計會影響到數據在HBase中的分佈,還會影響我們查詢效率,所以RowKey的設計質量決定了HBase的質量。是咱們大數據從業者必知必會的,自然也是面試必問的考察點。

那麼rowkey到底是什麼呢?原理是什麼呢?怎麼設計RowKey呢?使用場景是怎樣的呢?有哪些設計原則呢?又如何進行優化呢?

下面就讓我們帶着這些問題,一起探索RowKey的世界!
探索RowKey

二、RowKey的概念

RowKey從字面意思來看是行鍵的意思,咱們知道HBase可以理解爲一個nosql(not only sql)數據庫,既然是數據庫,那麼咱們日常使用最多的就是增刪改查(curd)。其實在增刪改查的過程中RowKey就充當了主鍵的作用,它和衆多的nosql數據庫一樣,可以唯一的標識一行記錄。

RowKey行鍵 (RowKey)可以是任意字符串,在HBase內部,RowKey保存爲字節數組。存儲時,數據按照RowKey的字典序(byte order)排序存儲。設計RowKey時,要充分利用排序存儲這個特性,將經常一起讀取的行存儲放到一起。

RowKey的特點小結如下:

  1. RowKey類似於主鍵,可以唯一的標識一行記錄;
  2. 由於數據按照RowKey的字典序(byte order)排序存儲,因此HBase中的數據永遠都是有序的。
  3. RowKey可以由用戶自己指定,只要保證這個字符串不重複就可以了。

知識點補充:在HBase中檢索數據時使用到RowKey的一共有三種方式:

  • get:通過指定單個RowKey來獲取對應的唯一一條記錄;
  • like:通過RowKey的range來進行匹配;
  • scan:通過設置startRow和stopRow參數來進行範圍匹配(注意:如果不設置就是全表掃描)。


三、RowKey的作用

要了解RowKey的作用,首先我們需要知道在HBase中,一個Region就相當於一個數據分片,每個Region都有StartRowKey和StopRowKey(用來表示 Region存儲的RowKey的範圍),HBase表裏面的數據是按照RowKey來分散存儲到不同的Region裏面的。

爲了避免熱點現象咱們需要將數據記錄均衡的分散到不同的Region中去,因此需要RowKey滿足這種散列的特點。此外,在數據讀寫過程中也是與RowKey密切相關的。RowKey的作用可以歸納如下:

  1. Hbase在讀寫數據時需要通過RowKey找到對應的Region;
  2. MemStore和HFile中的數據都是按照 RowKey 的字典序排序。

那到底啥是熱點現象呢?咱們接着分析!


四、熱點現象

4.1、熱點現象怎麼產生

我們知道HBase中的行是按照rowkey的字典順序排序的,這種設計優化了 scan操作,可以將相關的行以及會被一起讀取的行存取在臨近位置,便於 scan讀取。

然而萬事萬物都有兩面性,在咱們實際生產中,當大量請求訪問HBase集羣的一個或少數幾個節點,造成少數RegionServer的讀寫請求過多,負載過大,而其他RegionServer負載卻很小,這樣就造成熱點現象(吐槽:其實和數據傾斜類似,還整這麼高大上的名字)。

掌握了熱點現象的概念,我們就應該知道大量的訪問會使熱點Region所在的主機負載過大,引起性能下降,甚至導致Region不可用。所以我們在向HBase中插入數據的時候,應優化RowKey的設計,使數據被寫入集羣的多個region,而不是一個。儘量均衡地把記錄分散到不同的Region中去,平衡每個Region的壓力。

其實RowKey的優化主要就是在解決怎麼避免熱點現象。那麼有哪些避免熱點現象的方法呢?各有什麼缺點?帶着問題,接着往下看。

4.2、如何避免熱點現象(RowKey的優化)

在日常使用中,主要有3個方法來避免熱點現象,分別是反轉,加鹽和哈希。聽起來很奇怪,下面咱們逐個舉例詳細分析:

4.2.1、反轉(Reversing)

第一種咱們要分析的方法是反轉,顧名思義它就是把固定長度或者數字格式的 rowkey進行反轉,反轉分爲一般數據反轉和時間戳反轉,其中以時間戳反轉較常見。

適用場景:

比如咱們初步設計出的RowKey在數據分佈上不均勻,但RowKey尾部的數據卻呈現出了良好的隨機性(注意:隨機性強代表經常改變,沒意義,但分佈較好),此時,可以考慮將RowKey的信息翻轉,或者直接將尾部的bytes提前到RowKey的開頭。反轉可以有效的使RowKey隨機分佈,但是反轉後有序性肯定就得不到保障了,因此它犧牲了RowKey的有序性。

缺點:

利於Get操作,但不利於Scan操作,因爲數據在原RowKey上的自然順序已經被打亂。

舉例:

比如咱們通常會有需要快速獲取數據的最近版本的數據處理需求,這時候就需要把時間戳作爲RowKey來查詢了,但是時間戳正常情況下是這樣的:

1588610367373
1588610367396

前面這部分是相同的,在查詢的時候就容易造成熱點現象,因此需要使用時間戳反轉的方式來處理。實際生產中可以用 Long.Max_Value - timestamp 追加到 key 的末尾,比如 [key][reverse_timestamp], [key] 的最新值可以通過 scan [key]獲得[key]的第一條記錄,因爲HBase中RowKey是有序的,所以第一條記錄是最後錄入的數據。

常見的場景,比如需要保存一個用戶的操作記錄,就可以按照操作時間倒序排序,在設計rowkey的時候,可以這樣設計 [反轉後的userId][Long.Max_Value - timestamp],在查詢用戶的所有操作記錄數據的時候,直接指定反轉後的userId,startRow 是 [反轉後的userId][000000000000],stopRow 是 [反轉後的userId][Long.Max_Value - timestamp]。如果需要查詢某段時間的操作記錄,startRow 是[反轉後的userId[Long.Max_Value - 起始時間], stopRow 是[反轉後的userId][Long.Max_Value - 結束時間]

4.2.2、加鹽(Salting)

第二種咱們要介紹的方法是加鹽,玩過密碼學的可能知道密碼學裏也有加鹽的方法,但是咱們RowKey的加鹽和密碼學不一樣,它的原理是在原RowKey的前面添加固定長度的隨機數,也就是給RowKey分配一個隨機前綴使它和之前的RowKey的開頭不同。

適用場景:

比如咱們設計的RowKey是有意義的,但是數據類似,隨機性比較低,反轉也沒法保證隨機性,這樣就沒法根據RowKey分配到不同的Region裏,這時候就可以使用加鹽的方式了。

需要注意隨機數要能保障數據在所有Regions間的負載均衡,也就是說分配的隨機前綴的種類數量應該和你想把數據分散到的那些region的數量一致。只有這樣,加鹽之後的rowkey纔會根據隨機生成的前綴分散到各個region中,避免了熱點現象。

缺點:

大白話來理解就是加了鹽就嘗不到原有的味道了。因爲添加的是隨機數,添加後如果還基於原RowKey查詢,就無法知道隨機數是什麼,那樣在查詢的時候就需要去各個可能的Region中查找,同時加鹽對於讀取是利空的。並且加鹽這種方式增加了讀寫時的吞吐量。

4.2.3、哈希(Hashing)

最後介紹大家最熟悉的哈希方法,不管是學的啥技術,都會涉及到哈希,也都大同小異,比較簡單。

這裏的哈希是基於RowKey的完整或部分數據進行Hash,而後將哈希後的值完整替換或部分替換原RowKey的前綴部分。這裏說的hash常用的有MD5、sha1、sha256 或 sha512 等算法。

適用場景:

其實哈希和加鹽的適用場景類似,但是由於加鹽方法的前綴是隨機數,用原rowkey查詢時不方便,因此出現了哈希方法,由於哈希是使用各種常見的算法來計算出的前綴,因此哈希既可以使負載分散到整個集羣,又可以輕鬆讀取數據。

缺點:

與反轉類似,哈希也打亂了RowKey的自然順序,因此也不利於Scan。


五、RowKey設計原則

通過前面的分析我們應該知道了HBase中RowKey設計的重要性了,爲了幫助我們設計出完美的RowKey,HBase提出了RowKey的設計原則,一共有四點:長度原則、唯一原則、排序原則,散列原則

RowKey在字段的選擇上,需要遵循的最基本原則是唯一原則,因爲RowKey必須能夠唯一的識別一行數據。無論應用的負載特點是什麼樣,RowKey字段都應該首先考慮最高頻的查詢場景。數據庫通常都是以如何高效的讀取和消費數據爲目的,而不僅僅是數據存儲本身。然後再結合具體的負載特點,再對選取的RowKey字段值進行改造,結合RowKey的優化,也就是避免熱點現象的那些方法來優化就可以了。

5.1、長度原則

RowKey本質上是一個二進制碼的流,可以是任意字符串,最大長度爲64kb,實際應用中一般爲10-100byte,以byte[]數組形式保存,一般設計成定長。官方建議越短越好,不要超過16個字節,原因可以概括爲如下幾點:

  1. **影響HFile的存儲效率:**HBase裏的數據在持久化文件HFile中其實是按照Key-Value對形式存儲的。這時候如果RowKey很長,比如達到了200byte,那麼僅僅1000w行的記錄,只考慮RowKey就需佔用近2GB的空間,極大的影響了HFile的存儲效率。
  2. **降低檢索效率:**由於MemStore會緩存部分數據到內存中,如果RowKey比較長,就會導致內存的有效利用率降低,也就不能緩存更多的數據,從而降低檢索效率。
  3. **16字節是64位操作系統的最佳選擇:**64位系統,內存8字節對齊,控制在16字節,8字節的整數倍利用了操作系統的最佳特性。

5.2、唯一原則

其實唯一原則咱們可以結合HashMap的源碼設計或者主鍵的概念來理解,由於RowKey用來唯一標識一行記錄,所以必須在設計上保證RowKey的唯一性。

需要注意:由於HBase中數據存儲的格式是Key-Value對格式,所以如果向HBase中同一張表插入相同RowKey的數據,則原先存在的數據會被新的數據給覆蓋掉(和HashMap效果相同)。

5.3、排序原則

HBase會把RowKey按照ASCII進行自然有序排序,所以反過來我們在設計RowKey的時候可以根據這個特點來設計完美的RowKey,好好的利用這個特性就是排序原則。

5.4、散列原則

散列原則用大白話來講就是咱們設計出的RowKey需要能夠均勻的分佈到各個RegionServer上。

比如設計RowKey的時候,當Rowkey 是按時間戳的方式遞增,就不要將時間放在二進制碼的前面,可以將 Rowkey 的高位作爲散列字段,由程序循環生成,可以在低位放時間字段,這樣就可以提高數據均衡分佈在每個Regionserver實現負載均衡的機率。

結合前面分析的熱點現象的起因,思考:

如果沒有散列字段,首字段只有時間信息,那就會出現所有新數據都在一個 RegionServer上堆積的熱點現象,這樣在做數據檢索的時候負載將會集中在個別RegionServer上,不分散,就會降低查詢效率。

HBase裏的RowKey是按照字典序存儲,因此在設計RowKey時,咱們要充分利用這個排序特點,將經常一起讀取的數據存儲到一塊,將最近可能會被訪問的數據放在一塊。如果最近寫入HBase表中的數據是最可能被訪問的,可以考慮將時間戳作爲row key的一部分,由於是字典序排序,所以可以使用Long.MAX_VALUE - timestamp作爲row key,這樣能保證新寫入的數據在讀取時可以被快速找到。


六、總結

看到這裏RowKey的各個方面應該都已經搞懂了,本文從RowKey的原理,可能出現的問題,如何優化及各個優化措施對應的缺點和適用的場景,設計原則等角度對RowKey進行了詳細全面的解析,相信一定能對你有所幫助。

如果您對我的文章感興趣,歡迎關注點贊收藏,如果您有疑惑或發現文中有不對的地方,還請不吝賜教,非常感謝!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章