1. 程式人生 > >HBASE中column family的設計,rowkey的設計,以及row key的設計原則問題

HBASE中column family的設計,rowkey的設計,以及row key的設計原則問題

一、Hbase中的每條記錄的結構

Hbase的表組成:一個表可以理解成是行的集合,行(記錄)是列族的集合,列族是列的集合。

(1) 列族column family:它是column的集合,在建立表的時候就指定,不能頻繁修改。值得注意的是,列族的數量越少越好,因為過多的列族相互之間會影響,生產環境中的列族一般是一個到兩個。

資料的持久化檔案HFile中是按照Key-Value儲存的,同一個列族的所有列儲存在同一個底層儲存檔案裡。Hbase的資料在HDFS中的路徑結構如下:

hdfs://h201:8020/hbase/data/${名字空間}/${表名}/${區域名稱}/${列族名稱}/${檔名}

舉例:/hbase/data/ns1/t1/a4d63a61a8da24a863bff3c8d7cd20de/f1/c2a7fa8c41304b9e9b8b24b4a89171ce

其中{區域名稱}是t1的region, 由每張表切割形成,一張表由若干個region組成,不同的region分到不同的region server以便均衡負載

(2) 列column:和列族的限制數量不同,列族可以包含很多個列,前面說的“幾十億行*百萬列”就是這個意思。

(3) 列的值value:存在單元格(cell)中。每一列的值允許有多個版本,由timestamp來區分不同版本。多個版本產生原因:向同一行下面的同一個列多次插入資料,每插入一次就有一個對應版本的value。

從以下示例中可以看出habse儲存的資料格式

hbase(main):010:0>scan 'ns1:t1',{STARTROW => 'row1', LIMIT => 5}

ROW                           COLUMN+CELL                                                                                                                        

row10                         column=f1:age,timestamp=1490608685532,value=\x00\x00\x00\x0A

row10                         column=f1:id,timestamp=1490608685532,value=\x00\x00\x00\x0A

row10                         column=f1:name,timestamp=1490608685532,value=tonykidkid10

row100                        column=f1:age,timestamp=1490608685532,value=\x00\x00\x00\x00

row100                        column=f1:id,timestamp=1490608685532,value=\x00\x00\x00d

row100                        column=f1:name,timestamp=1490608685532,value=tonykidkid100

row1000                       column=f1:age,timestamp=1490608685532,value=\x00\x00\x00\x00

row1000                       column=f1:id,timestamp=1490608685532,value=\x00\x00\x03\xE8

row1000                       column=f1:name,timestamp=1490608685532,value=tonykidkid1000 

row1001                       column=f1:age,timestamp=1490608685532,value=\x00\x00\x00\x01

row1001                       column=f1:id,timestamp=1490608685532,value=\x00\x00\x03\xE9

row1001                       column=f1:name,timestamp=1490608685532,value=tonykidkid1001 

row1002                       column=f1:age,timestamp=1490608685532,value=\x00\x00\x00\x02

row1002                       column=f1:id,timestamp=1490608685532,value=\x00\x00\x03\xEA

row1002                       column=f1:name,timestamp=1490608685532,value=tonykidkid1002

5 row(s) in0.0550 seconds

以row1002這一條記錄來說明——

         row1002是row key .row key在hbase裡是唯一的,而且只出現一次,否則就是在更新同一行。也就是說有幾個不同的row key就有幾條不同的記錄。我們可以通過不同的行健來增加多行記錄。行健的唯一性這個特性類似於關係型資料庫的主鍵。

column=f1:name, timestamp=1490608685532,value=tonykidkid1002表示列族f1包含name列,“列族+列名”決定了不同的列。

Timestamp是時間戳,表示此列對應值的版本, 預設VERSIONS=1,value就是列族f1下name的值了。

需要明確的一點,hbase是通過3個維度來對記錄進行快速定位:行健 + (列族+列名) + 時間戳,即:

                   row key àcolumn family + qualifier à timestamp

結合上面的例子,t1表的每一行有3個column, 分別是age,id, name.  比如我想查rowkey為row1002的name的值,命令列下的查詢語法:

hbase(main):021:0>get 'ns1:t1', 'row1002' ,'f1:name'

COLUMN                 CELL                                                          

 f1:name               timestamp=1490608685532,value=tonykidkid1002                 

1 row(s) in0.0420 seconds

         或者這樣查也是對的:

hbase(main):022:0>get 'ns1:t1', 'row1002' , {COLUMN => 'f1:name'}

COLUMN                 CELL                                                           

 f1:name               timestamp=1490608685532,value=tonykidkid1002                 

1 row(s) in0.0300 seconds

二、Hbase中的行健Row Key

我們知道HBase是採用Key-Value格式來儲存資料,那麼Row key就是Key-Value中的Key了,key不能重複,所以表示唯一一行。

Rowkey是表記錄在hbase表中的唯一標識,作為檢索表記錄的唯一“主鍵”。hbase載入資料時,也是根據row key的二進位制順序由小到大進行的。

Row key的最大長度為64KB,它是一段二進位制碼流(byte[ ]),所以任何資料型別都可以用來做row key,內容可以由我們使用者自定義、自設計。

HBase根據Row key來進行檢索,系統通過找到某個Row key (或者某個 Row key 範圍)所在的Region,然後將查詢資料的請求路由到該Region獲取此條記錄。HBase的檢索有3種方式:

1,通過get方式,指定rowkey獲取唯一一條記錄

2,通過scan方式,設定起始行和結束行引數進行範圍匹配

3,全表掃描,即直接掃描整張表中所有行記錄

Hbase對記錄的排序row key可以是任意的位元組陣列byte []

row key按照字典順序排序的規則:在字典順序中按照二進位制逐個位元組、從左到右對比每一個row key,例如row1001小於row1002,rowxxa小於rowxxb等等。這種設計優化了scan,可以將相關的行以及會被一起讀取的行存在相近位置,便於scan。

三、Hbase中的 “熱點”問題

hbase中的熱點現象:

我們知道,檢索habse的記錄首先要通過row key來定位資料行。當大量的client訪問hbase叢集的一個或少數幾個節點,造成少數region server的讀/寫請求過多、負載過大,而其他region server負載卻很小,就造成了“熱點”現象。

熱點的危害:

大量訪問會使熱點region所在的單個主機負載過大,引起效能下降甚至region不可用。

熱點產生原因:

有大量連續編號的row key  ==>  大量row key相近的記錄集中在個別region

 ==>  client檢索記錄時,對個別region訪問過多  ==>  此region所在的主機過載

 ==>  熱點

明白了熱點原因就可以從row key著手解決,下面幾個方法可以使用,目的就一個:儘量均衡地把每一條記錄分散到不同的region裡去!

下面是一些常見的避免熱點的方法以及它們的優缺點:

加鹽

       這裡所說的加鹽不是密碼學中的加鹽,而是在rowkey的前面增加隨機數,具體就是給rowkey分配一個隨機字首以使得它和之前的rowkey的開頭不同。給多少個字首?這個數量應該和我們想要分散資料到不同的region的數量一致(類似hive裡面的分桶)。加鹽之後的rowkey就會根據隨機生成的字首分散到各個region上,以避免熱點。

雜湊

       雜湊會使同一行永遠用一個字首加鹽。雜湊也可以使負載分散到整個叢集,但是讀卻是可以預測的。使用確定的雜湊可以讓客戶端重構完整的rowkey,可以使用get操作準確獲取某一個行資料。

反轉

       第三種防止熱點的方法是反轉固定長度或者數字格式的rowkey。這樣可以使得rowkey中經常改變的部分(最沒有意義的部分)放在前面。這樣可以有效的隨機rowkey,但是犧牲了rowkey的有序性。

反轉rowkey的例子:以手機號為rowkey,可以將手機號反轉後的字串作為rowkey,從而避免諸如139、158之類的固定號碼開頭導致的熱點問題。

時間戳反轉

       一個常見的資料處理問題是快速獲取資料的最近版本,使用反轉的時間戳作為rowkey的一部分對這個問題十分有用,可以用Long.Max_Value - timestamp追加到key的末尾,例如[key][reverse_timestamp] ,[key] 的最新值可以通過scan [key]獲得[key]的第一條記錄,因為HBase中rowkey是有序的,第一條記錄是最後錄入的資料。

儘量減少行和列的大小

       在HBase中,value永遠和它的key一起傳輸的。當具體的值在系統間傳輸時,它的rowkey,列名,時間戳也會一起傳輸。如果你的rowkey和列名很大,HBase storefiles中的索引(有助於隨機訪問)會佔據HBase分配的大量記憶體,因為具體的值和它的key很大。可以增加block大小使得storefiles索引再更大的時間間隔增加,或者修改表的模式以減小rowkey和列名的大小。壓縮也有助於更大的索引。

其他辦法    

列族名的長度儘可能小,最好是隻有一個字元。冗長的屬性名雖然可讀性好,但是更短的屬性名儲存在HBase中會更好。也可以在建表時預估資料規模,預留region數量,例如create 'myspace:mytable’, SPLITS => [01,02,03,,...99]

總結一下,row key的設計原則應當遵循以下幾點

(1)rowkey唯一原則

必須在設計上保證其唯一性,rowkey是按照二進位制位元組陣列排序儲存的,因此,設計rowkey的時候,要充分利用這個排序的特點,將經常讀取的資料儲存到一塊,將最近可能會被訪問的資料放到一塊。所以設計rwo key時儘量把體現業務特徵的資訊、業務上有唯一性的資訊編進row key。

(2)rowkey長度原則

rowkey是一個二進位制碼流,可以是任意字串,最大長度 64kb ,實際應用中一般為10-100byte,以byte[] 形式儲存,一般設計成定長。建議越短越好,不要超過16個位元組,2個原因——

原因1:

資料的持久化檔案HFile中是按照(Key,Value)儲存的,如果rowkey過長,例如超過100byte,那麼1000萬行的記錄計算,僅row key就需佔用100*1000萬=10億byte,近1Gb。這樣會極大影響HFile的儲存效率!

原因2:

MemStore將快取部分資料到記憶體,若 rowkey欄位過長,記憶體的有效利用率就會降低,就不能快取更多的資料,從而降低檢索效率。

目前作業系統都是64位系統,記憶體8位元組對齊,控制在16個位元組,8位元組的整數倍利用了作業系統的最佳特性。

(3)rowkey雜湊原則

如果rowkey按照時間戳的方式遞增,不要將時間放在二進位制碼的前面,建議將rowkey的高位作為雜湊欄位,由程式隨機生成,低位放時間欄位,這樣將提高資料均衡分佈在每個RegionServer,以實現負載均衡的機率。如果沒有雜湊欄位,首欄位直接是時間資訊,所有的資料都會集中在一個RegionServer上,這樣在資料檢索的時候負載會集中在個別的RegionServer上,造成熱點問題,會降低查詢效率。