HBase的RowKey設計

from:  https://blog.csdn.net/silentwolfyh/article/details/51620724#t1

閱讀目錄


1 概述

HBase是一個分佈式的、面向列的數據庫,它和一般關係型數據庫的最大區別是:HBase很適合於存儲非結構化的數據,還有就是它基於列的而不是基於行的模式。

既然HBase是採用KeyValue的列存儲,那Rowkey就是KeyValue的Key了,表示唯一一行。Rowkey也是一段二進制碼流,最大長度爲64KB,內容可以由使用的用戶自定義。數據加載時,一般也是根據Rowkey的二進制序由小到大進行的。

HBase是根據Rowkey來進行檢索的,系統通過找到某個Rowkey (或者某個 Rowkey 範圍)所在的Region,然後將查詢數據的請求路由到該Region獲取數據。HBase的檢索支持3種方式:

(1) 通過單個Rowkey訪問,即按照某個Rowkey鍵值進行get操作,這樣獲取唯一一條記錄;

(2) 通過Rowkey的range進行scan,即通過設置startRowKey和endRowKey,在這個範圍內進行掃描。這樣可以按指定的條件獲取一批記錄;

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

HBASE按單個Rowkey檢索的效率是很高的,耗時在1毫秒以下,每秒鐘可獲取1000~2000條記錄,不過非key列的查詢很慢。

2 HBase的RowKey設計

2.1 設計原則

2.1.1 Rowkey長度原則

Rowkey是一個二進制碼流,Rowkey的長度被很多開發者建議說設計在10~100個字節,不過建議是越短越好,不要超過16個字節。

原因如下:

(1)數據的持久化文件HFile中是按照KeyValue存儲的,如果Rowkey過長比如100個字節,1000萬列數據光Rowkey就要佔用100*1000萬=10億個字節,將近1G數據,這會極大影響HFile的存儲效率;

(2)MemStore將緩存部分數據到內存,如果Rowkey字段過長內存的有效利用率會降低,系統將無法緩存更多的數據,這會降低檢索效率。因此Rowkey的字節長度越短越好。

(3)目前操作系統是都是64位系統,內存8字節對齊。控制在16個字節,8字節的整數倍利用操作系統的最佳特性。

2.1.2 Rowkey散列原則

如果Rowkey是按時間戳的方式遞增,不要將時間放在二進制碼的前面,建議將Rowkey的高位作爲散列字段,由程序循環生成,低位放時間字段,這樣將提高數據均衡分佈在每個Regionserver實現負載均衡的機率。如果沒有散列字段,首字段直接是時間信息將產生所有新數據都在一個 RegionServer上堆積的熱點現象,這樣在做數據檢索的時候負載將會集中在個別RegionServer,降低查詢效率。

2.1.3 Rowkey唯一原則

必須在設計上保證其唯一性。

2.2 應用場景

基於Rowkey的上述3個原則,應對不同應用場景有不同的Rowkey設計建議。

2.2.1 針對事務數據Rowkey設計

事務數據是帶時間屬性的,建議將時間信息存入到Rowkey中,這有助於提示查詢檢索速度。對於事務數據建議缺省就按天爲數據建表,這樣設計的好處是多方面的。按天分表後,時間信息就可以去掉日期部分只保留小時分鐘毫秒,這樣4個字節即可搞定。加上散列字段2個字節一共6個字節即可組成唯一 Rowkey。如下圖所示:

事務數據Rowkey設計
第0字節 第1字節 第2字節 第3字節 第4字節 第5字節
散列字段 時間字段(毫秒) 擴展字段
0~65535(0x0000~0xFFFF) 0~86399999(0x00000000~0x05265BFF)

這樣的設計從操作系統內存管理層面無法節省開銷,因爲64位操作系統是必須8字節對齊。但是對於持久化存儲中Rowkey部分可以節省25%的開銷。也許有人要問爲什麼不將時間字段以主機字節序保存,這樣它也可以作爲散列字段了。這是因爲時間範圍內的數據還是儘量保證連續,相同時間範圍內的數據查找的概率很大,對查詢檢索有好的效果,因此使用獨立的散列字段效果更好,對於某些應用,我們可以考慮利用散列字段全部或者部分來存儲某些數據的字段信息,只要保證相同散列值在同一時間(毫秒)唯一。

2.2.2 針對統計數據的Rowkey設計

統計數據也是帶時間屬性的,統計數據最小單位只會到分鐘(到秒預統計就沒意義了)。同時對於統計數據我們也缺省採用按天數據分表,這樣設計的好處無需多說。按天分表後,時間信息只需要保留小時分鐘,那麼0~1400只需佔用兩個字節即可保存時間信息。由於統計數據某些維度數量非常龐大,因此需要4個字節作爲序列字段,因此將散列字段同時作爲序列字段使用也是6個字節組成唯一Rowkey。如下圖所示:

統計數據Rowkey設計
第0字節 第1字節 第2字節 第3字節 第4字節 第5字節
散列字段(序列字段) 時間字段(分鐘) 擴展字段
0x00000000~0xFFFFFFFF) 0~1439(0x0000~0x059F)

同樣這樣的設計從操作系統內存管理層面無法節省開銷,因爲64位操作系統是必須8字節對齊。但是對於持久化存儲中Rowkey部分可以節省25%的開銷。預統計數據可能涉及到多次反覆的重計算要求,需確保作廢的數據能有效刪除,同時不能影響散列的均衡效果,因此要特殊處理。

2.2.3 針對通用數據的Rowkey設計

通用數據採用自增序列作爲唯一主鍵,用戶可以選擇按天建分表也可以選擇單表模式。這種模式需要確保同時多個入庫加載模塊運行時散列字段(序列字段)的唯一性。可以考慮給不同的加載模塊賦予唯一因子區別。設計結構如下圖所示。

通用數據Rowkey設計
第0字節 第1字節 第2字節 第3字節
散列字段(序列字段) 擴展字段(控制在12字節內)
0x00000000~0xFFFFFFFF) 可由多個用戶字段組成

2.2.4 支持多條件查詢的RowKey設計

HBase按指定的條件獲取一批記錄時,使用的就是scan方法。 scan方法有以下特點:

(1)scan可以通過setCaching與setBatch方法提高速度(以空間換時間);

(2)scan可以通過setStartRow與setEndRow來限定範圍。範圍越小,性能越高。

通過巧妙的RowKey設計使我們批量獲取記錄集合中的元素挨在一起(應該在同一個Region下),可以在遍歷結果時獲得很好的性能。

(3)scan可以通過setFilter方法添加過濾器,這也是分頁、多條件查詢的基礎。

在滿足長度、三列、唯一原則後,我們需要考慮如何通過巧妙設計RowKey以利用scan方法的範圍功能,使得獲取一批記錄的查詢速度能提高。下例就描述如何將多個列組合成一個RowKey,使用scan的range來達到較快查詢速度。

例子:

我們在表中存儲的是文件信息,每個文件有5個屬性:文件id(long,全局唯一)、創建時間(long)、文件名(String)、分類名(String)、所有者(User)。

我們可以輸入的查詢條件:文件創建時間區間(比如從20120901到20120914期間創建的文件),文件名(“中國好聲音”),分類(“綜藝”),所有者(“浙江衛視”)。

假設當前我們一共有如下文件:

ID CreateTime Name Category UserID
1 20120902 中國好聲音第1期 綜藝 1
2 20120904 中國好聲音第2期 綜藝 1
3 20120906 中國好聲音外卡賽 綜藝 1
4 20120908 中國好聲音第3期 綜藝 1
5 20120910 中國好聲音第4期 綜藝 1
6 20120912 中國好聲音選手採訪 綜藝花絮 2
7 20120914 中國好聲音第5期 綜藝 1
8 20120916 中國好聲音錄製花絮 綜藝花絮 2
9 20120918 張瑋獨家專訪 花絮 3
10 20120920 加多寶涼茶廣告 綜藝廣告 4

這裏UserID應該對應另一張User表,暫不列出。我們只需知道UserID的含義:

1代表 浙江衛視; 2代表 好聲音劇組; 3代表 XX微博; 4代表贊助商。調用查詢接口的時候將上述5個條件同時輸入find(20120901,20121001,”中國好聲音”,”綜藝”,”浙江衛視”)。此時我們應該得到記錄應該有第1、2、3、4、5、7條。第6條由於不屬於“浙江衛視”應該不被選中。我們在設計RowKey時可以這樣做:採用 UserID + CreateTime + FileID組成RowKey,這樣既能滿足多條件查詢,又能有很快的查詢速度。

需要注意以下幾點:

(1)每條記錄的RowKey,每個字段都需要填充到相同長度。假如預期我們最多有10萬量級的用戶,則userID應該統一填充至6位,如000001,000002…

(2)結尾添加全局唯一的FileID的用意也是使每個文件對應的記錄全局唯一。避免當UserID與CreateTime相同時的兩個不同文件記錄相互覆蓋。

按照這種RowKey存儲上述文件記錄,在HBase表中是下面的結構:

rowKey(userID 6 + time 8 + fileID 6) name category ….

00000120120902000001

00000120120904000002

00000120120906000003

00000120120908000004

00000120120910000005

00000120120914000007

00000220120912000006

00000220120916000008

00000320120918000009

00000420120920000010

怎樣用這張表?

在建立一個scan對象後,我們setStartRow(00000120120901),setEndRow(00000120120914)。

這樣,scan時只掃描userID=1的數據,且時間範圍限定在這個指定的時間段內,滿足了按用戶以及按時間範圍對結果的篩選。並且由於記錄集中存儲,性能很好。

然後使用 SingleColumnValueFilter(org.apache.hadoop.hbase.filter.SingleColumnValueFilter),共4個,分別約束name的上下限,與category的上下限。滿足按同時按文件名以及分類名的前綴匹配。

(注意:使用SingleColumnValueFilter會影響查詢性能,在真正處理海量數據時會消耗很大的資源,且需要較長的時間)

如果需要分頁還可以再加一個PageFilter限制返回記錄的個數。

以上,我們完成了高性能的支持多條件查詢的HBase表結構設計。


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