由於一直在作hbase的應用層面的開發,因此體會的比較深的一點是hbase的表結構設計會對系統的性能以及開銷上形成很大的區別,本篇文章先按照hbase表中的rowkey、columnfamily、column、timestamp幾個方面進行一些分析。最後結合分析如何設計一種適合應用的高效表結構。redis
一、表的屬性算法
(1)最大版本數:一般是3,若是對於更新比較頻繁的應用徹底能夠設置爲1,可以快速的淘汰無用數據,對於節省存儲空間和提升查詢速度有效果。不過這類需求在海量數據領域比較小衆。數據庫
(2)壓縮算法:能夠嘗試一下最新出爐的snappy算法,相對lzo來講,壓縮率接近,壓縮效率稍高,解壓效率高不少。數據結構
(3)inmemory:表在內存中存放,一直會被忽略的屬性。若是徹底將數據存放在內存中,那麼hbase和如今流行的內存數據庫memorycached和redis性能差距有多少,尚待實測。app
(4)bloomfilter:根據應用來定,看須要精確到rowkey仍是column。不過這裏須要理解一下原理,bloomfilter的做用是對一個region下查找記錄所在的hfile有用。即若是一個region下的hfile數量不少,bloomfilter的做用越明顯。適合那種compaction趕不上flush速度的應用。ide
二、rowkeyoop
2.1 排序問題性能
數字rowkey的從大到小排序:原生hbase只支持從小到大的排序,這樣就對於排行榜一類的查詢需求很尷尬。那麼採用rowkey = Integer.MAX_VALUE-rowkey的方式將rowkey進行轉換,最大的變最小,最小的變最大。在應用層再轉回來便可完成排序需求。測試
2.2 熱點問題
HBase中的行是按照rowkey的字典順序排序的,這種設計優化了scan操做,能夠將相關的行以及會被一塊兒讀取的行存取在臨近位置,便於scan。然而糟糕的rowkey設計是熱點的源頭。 熱點發生在大量的client直接訪問集羣的一個或極少數個節點(訪問多是讀,寫或者其餘操做)。大量訪問會使熱點region所在的單個機器超出自身承受能力,引發性能降低甚至region不可用,這也會影響同一個RegionServer上的其餘region,因爲主機沒法服務其餘region的請求。 設計良好的數據訪問模式以使集羣被充分,均衡的利用。優化
爲了不寫熱點,設計rowkey使得不一樣行在同一個region,可是在更多數據狀況下,數據應該被寫入集羣的多個region,而不是一個。
下面是一些常見的避免熱點的方法以及它們的優缺點:
加鹽
這裏所說的加鹽不是密碼學中的加鹽,而是在rowkey的前面增長隨機數,具體就是給rowkey分配一個隨機前綴以使得它和以前的rowkey的開頭不一樣。分配的前綴種類數量應該和你想使用數據分散到不一樣的region的數量一致。加鹽以後的rowkey就會根據隨機生成的前綴分散到各個region上,以免熱點。
哈希
哈希會使同一行永遠用一個前綴加鹽。哈希也可使負載分散到整個集羣,可是讀倒是能夠預測的。使用肯定的哈希可讓客戶端重構完整的rowkey,可使用get操做準確獲取某一個行數據
反轉
第三種防止熱點的方法時反轉固定長度或者數字格式的rowkey。這樣可使得rowkey中常常改變的部分(最沒有意義的部分)放在前面。這樣能夠有效的隨機rowkey,可是犧牲了rowkey的有序性。
反轉rowkey的例子以手機號爲rowkey,能夠將手機號反轉後的字符串做爲rowkey,這樣的就避免了以手機號那樣比較固定開頭致使熱點問題
時間戳反轉
一個常見的數據處理問題是快速獲取數據的最近版本,使用反轉的時間戳做爲rowkey的一部分對這個問題十分有用,能夠用 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是[user反轉][Long.Max_Value - 起始時間],stopRow是[userId反轉][Long.Max_Value - 結束時間] rowkey是hbase的key-value存儲中的key,一般使用用戶要查詢的字段做爲rowkey,查詢結果做爲value。能夠經過設計知足幾種不一樣的查詢需求。
三、columnfamily
columnfamily儘可能少,緣由是過多的columnfamily之間會互相影響。
四、column
對於column須要擴展的應用,column能夠按普通的方式設計,可是對於列相對固定的應用,最好採用將一行記錄封裝到一個column中的方式,這樣可以節省存儲空間。封裝的方式推薦protocolbuffer。
如下會分場景介紹一些特殊的表結構設計方法,只是一些摸索,歡迎討論:
value數目過多場景下的表結構設計:
目前我碰到了一種key-value的數據結構,某一個key下面包含的column不少,以至於客戶端查詢的時候oom,bulkload寫入的時候oom,regionsplit的時候失敗這三種後果。一般來說,hbase的column數目不要超過百萬這個數量級。在官方的說明和我實際的測試中都驗證了這一點。
有兩種思路能夠參考,第一種是單獨處理這些特殊的rowkey,第二種以下:
能夠考慮將column設計到rowkey的方法解決。例如原來的rowkey是uid1,,column是uid2,uid3...。從新設計以後rowkey爲<uid1>~<uid2>,<uid1>~<uid3>...固然你們會有疑問,這種方式如何查詢,若是要查詢uid1下面的全部uid怎麼辦。這裏說明一下hbase並非只有get一種隨機讀取的方法。而是含有scan(startkey,endkey)的掃描方法,而這種方法和get的效率至關。須要取得uid1下的記錄只須要new Scan("uid1~","uid1~~")便可。
這裏的設計靈感來自於hadoop world大會上的一篇文章,這篇文章自己也很棒,推薦你們看一下http://www.cloudera.com/resource/hadoop-world-2011-presentation-slides-advanced-hbase-schema-design/
拓展閱讀:
HBase 在淘寶的應用和優化 http://www.iteye.com/magazines/83