原文: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"。
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。
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)可以在表創建完以后動態插入數據時添加
