HBase之Rowkey設計總結及易觀方舟實戰篇
置頂 2018年06月02日 21:52:46 代立冬 閱讀數:1699 標籤: Rowkey設計經驗hbase經驗總結易觀方舟rowkey設計實踐rowkey實戰 更多
個人分類: ●HBase--------【HBase優化】
所屬專欄: 大資料實戰系列
版權宣告:*************本文為博主原創文章,轉載請註明出處************* https://blog.csdn.net/oDaiLiDong/article/details/80551851
一、引言
HBase由於其儲存和讀寫的高效能,在OLAP即時分析中越來越發揮重要的作用,在易觀精細化運營產品--易觀方舟也有廣泛的應用。作為Nosql資料庫的一員,HBase查詢只能通過其Rowkey來查詢(Rowkey用來表示唯一一行記錄),Rowkey設計的優劣直接影響讀寫效能。HBase中的資料是按照Rowkey的ASCII字典順序進行全域性排序的,有夥伴可能對ASCII字典序印象不夠深刻,下面舉例說明:
假如有5個Rowkey:"012", "0", "123", "234", "3",按ASCII字典排序後的結果為:"0", "012", "123", "234", "3"。(注:文末附常用ASCII碼錶)
Rowkey排序時會先比對兩個Rowkey的第一個位元組,如果相同,然後會比對第二個位元組,依次類推... 對比到第X個位元組時,已經超出了其中一個Rowkey的長度,短的Rowkey排在前面。
由於HBase是通過Rowkey查詢的,一般Rowkey上都會存一些比較關鍵的檢索資訊,我們需要提前想好資料具體需要如何查詢,根據查詢方式進行資料儲存格式的設計,要避免做全表掃描,因為效率特別低。
二、Rowkey設計原則
Rowkey設計應遵循以下原則:
1.Rowkey的唯一原則
必須在設計上保證其唯一性。由於在HBase中資料儲存是Key-Value形式,若HBase中同一表插入相同Rowkey,則原先的資料會被覆蓋掉(如果表的version設定為1的話),所以務必保證Rowkey的唯一性
2. Rowkey的排序原則
HBase的Rowkey是按照ASCII有序設計的,我們在設計Rowkey時要充分利用這點。比如視訊網站上對影片《泰坦尼克號》的彈幕資訊,這個彈幕是按照時間倒排序展示視訊裡,這個時候我們設計的Rowkey要和時間順序相關。可以使用"Long.MAX_VALUE - 彈幕發表時間"的 long 值作為 Rowkey 的字首
3. Rowkey的雜湊原則
我們設計的Rowkey應均勻的分佈在各個HBase節點上。拿常見的時間戳舉例,假如Rowkey是按系統時間戳的方式遞增,Rowkey的第一部分如果是時間戳資訊的話將造成所有新資料都在一個RegionServer上堆積的熱點現象,也就是通常說的Region熱點問題, 熱點發生在大量的client直接訪問集中在個別RegionServer上(訪問可能是讀,寫或者其他操作),導致單個RegionServer機器自身負載過高,引起效能下降甚至Region不可用,常見的是發生jvm full gc或者顯示region too busy異常情況,當然這也會影響同一個RegionServer上的其他Region。
通常有3種辦法來解決這個Region熱點問題:
ΩΩ1、Reverse反轉
針對固定長度的Rowkey反轉後儲存,這樣可以使Rowkey中經常改變的部分放在最前面,可以有效的隨機Rowkey。
反轉Rowkey的例子通常以手機舉例,可以將手機號反轉後的字串作為Rowkey,這樣的就避免了以手機號那樣比較固定開頭(137x、15x等)導致熱點問題,
這樣做的缺點是犧牲了Rowkey的有序性。
ΩΩ2、Salt加鹽
Salt是將每一個Rowkey加一個字首,字首使用一些隨機字元,使得資料分散在多個不同的Region,達到Region負載均衡的目標。
比如在一個有4個Region(注:以 [ ,a)、[a,b)、[b,c)、[c, )為Region起至)的HBase表中,
加Salt前的Rowkey:abc001、abc002、abc003
我們分別加上a、b、c字首,加Salt後Rowkey為:a-abc001、b-abc002、c-abc003
可以看到,加鹽前的Rowkey預設會在第2個region中,加鹽後的Rowkey資料會分佈在3個region中,理論上處理後的吞吐量應是之前的3倍。由於字首是隨機的,讀這些資料時需要耗費更多的時間,所以Salt增加了寫操作的吞吐量,不過缺點是同時增加了讀操作的開銷。
ΩΩ3、Hash雜湊或者Mod
用Hash雜湊來替代隨機Salt字首的好處是能讓一個給定的行有相同的字首,這在分散了Region負載的同時,使讀操作也能夠推斷。確定性Hash(比如md5後取前4位做字首)能讓客戶端重建完整的RowKey,可以使用get操作直接get想要的行。
例如將上述的原始Rowkey經過hash處理,此處我們採用md5雜湊演算法取前4位做字首,結果如下
9bf0-abc001 (abc001在md5後是9bf049097142c168c38a94c626eddf3d,取前4位是9bf0)
7006-abc002
95e6-abc003
若以前4個字元作為不同分割槽的起止,上面幾個Rowkey資料會分佈在3個region中。實際應用場景是當資料量越來越大的時候,這種設計會使得分割槽之間更加均衡。
如果Rowkey是數字型別的,也可以考慮Mod方法。
4. Rowkey的長度原則
Rowkey長度設計原則:Rowkey是一個二進位制,Rowkey的長度被很多開發者建議說設計在10~100個位元組,建議是越短越好。
原因有兩點:
其一是HBase的持久化檔案HFile是按照KeyValue儲存的,如果Rowkey過長比如500個位元組,1000萬列資料光Rowkey就要佔用500*1000萬=50億個位元組,將近1G資料,這會極大影響HFile的儲存效率
其二是MemStore快取部分資料到記憶體,如果Rowkey欄位過長記憶體的有效利用率會降低,系統無法快取更多的資料,這會降低檢索效率
需要指出的是不僅Rowkey的長度是越短越好,而且列族名、列名等儘量使用短名字,因為HBase屬於列式資料庫,這些名字都是會寫入到HBase的持久化檔案HFile中去,過長的Rowkey、列族、列名都會導致整體的儲存量成倍增加。
三、方舟HBase Rowkey設計實戰
在實際的設計中我們可能更多的是結合多種設計方法來實現Rowkey的最優化設計,比如設計訂單狀態表時使用:Rowkey: reverse(order_id) + (Long.MAX_VALUE – timestamp),這樣設計的好處一是通過reverse訂單號避免Region熱點,二是可以按時間倒排顯示。
結合易觀方舟使用HBase作為事件(事件指的的終端在APP中發生的行為,比如登入、下單等等統稱事件(event))的臨時儲存(HBase只儲存了最近10分鐘的熱資料)來舉例:
設計event事件的Rowkey為:兩位隨機數Salt + eventId + Date + kafka的Offset
這樣設計的好處是:
設計加鹽的目的是為了增加查詢的併發性,假如Salt的範圍是0~n,那我們在查詢的時候,可以將資料分為n個split同時做scan操作。經過我們的多次測試驗證,增加併發度能夠將整體的查詢速度提升5~20倍以上。隨後的eventId和Date是用來做範圍Scan使用的。在我們的查詢場景中,大部分都是指定了eventId的,因此我們把eventId放在了第二個位置上,同時呢,eventId的取值有幾十個,通過Salt + eventId的方式可以保證不會形成熱點。在單機部署版本中,HBase會儲存所有的event資料,所以我們把date放在rowkey的第三個位置上以實現按date做scan,批量Scan效能甚至可以做到毫秒級返回。
這樣的rowkey設計能夠很好的支援如下幾個查詢場景:
1、全表scan
在這種情況下,我們仍然可以將全表資料切分成n份併發查詢,從而實現查詢的實時響應。
2、只按照event_id查詢
3、按照event_id和date查詢
此外易觀方舟也使用HBase做使用者畫像的標籤儲存方案,儲存每個app的使用者的人口學屬性和商業屬性等標籤資訊,由於其設計的更為複雜,後續會另起篇幅詳細展開。
最後我們順帶提下HBase的表設計,HBase表設計通常可以是寬表(wide table)模式,即一行包括很多列。同樣的資訊也可以用高表(tall table)形式儲存,通常高表的效能比寬表要高出 50%以上,所以推薦大家使用高表來完成表設計。表設計時,我們也應該要考慮HBase資料庫的一些特性:
1、在HBase表中是通過Rowkey的字典序來進行資料排序的
2、所有儲存在HBase表中的資料都是二進位制的位元組
3、原子性只在行內保證,HBase不支援跨行事務
4、列族(Column Family)在表建立之前就要定義好
5. 列族中的列標識(Column Qualifier)可以在表建立完以後動態插入資料時新增
四、總結
在做Rowkey設計時,請先考慮業務是讀比寫多、還是讀比寫少,HBase本身是為寫優化的,即便是這樣,也可能會出現熱點問題,而如果我們讀比較多的話,除了考慮以上Rowkey設計原則外,還可以考慮HBase的Coprocessor甚至elastic search結合的方法,無論哪種方式,都建議做實際業務場景下資料的壓力測試以得到最優結果。
附:常用ASCII碼錶
ASCII值 |
字元 |
ASCII值 |
字元 |
ASCII值 |
字元 |
32 |
(space) |
64 |
@ |
96 |
、 |
33 |
! |
65 |
A |
97 |
a |
34 |
" |
66 |
B |
98 |
b |
35 |
# |
67 |
C |
99 |
c |
36 |
$ |
68 |
D |
100 |
d |
37 |
% |
69 |
E |
101 |
e |
38 |
& |
70 |
F |
102 |
f |
39 |
, |
71 |
G |
103 |
g |
40 |
( |
72 |
H |
104 |
h |
41 |
) |
73 |
I |
105 |
i |
42 |
* |
74 |
J |
106 |
j |
43 |
+ |
75 |
K |
107 |
k |
44 |
, |
76 |
L |
108 |
l |
45 |
- |
77 |
M |
109 |
m |
46 |
. |
78 |
N |
110 |
n |
47 |
/ |
79 |
O |
111 |
o |
48 |
0 |
80 |
P |
112 |
p |
49 |
1 |
81 |
Q |
113 |
q |
50 |
2 |
82 |
R |
114 |
r |
51 |
3 |
83 |
S |
115 |
s |
52 |
4 |
84 |
T |
116 |
t |
53 |
5 |
85 |
U |
117 |
u |
54 |
6 |
86 |
V |
118 |
v |
55 |
7 |
87 |
W |
119 |
w |
56 |
8 |
88 |
X |
120 |
x |
57 |
9 |
89 |
Y |
121 |
y |
58 |
: |
90 |
Z |
122 |
z |
59 |
; |
91 |
[ |
123 |
{ |
60 |
< |
92 |
/ |
124 |
| |
61 |
= |
93 |
] |
125 |
} |
62 |
> |
94 |
^ |
126 |
` |
63 |
? |
95 |
_ |