Hbase生產實踐

???????????????????????????????????? Hbase生產實踐

背景

HBase是一個分布式的、面向列的開源數據庫,它是hadoop生態圈的一員,有海量數據存儲能力,對資源的消耗也相對較小,但同時查詢能力也有局限,因此如何正確的使用hbase非常關鍵。

關于hbase

一個表可以有上億行,上百萬列

面向列(族)的存儲和權限控制,列(族)獨立檢索。

對于為空(null)的列,并不占用存儲空間,因此,表可以設計的非常稀疏。

選擇完成時,被選擇的列要重新組裝

INSERT/UPDATE比較麻煩

Hbase基本概念

Region是HBase中分布式存儲和負載均衡的最小單元。不同Region分布到不同RegionServer上,但并不是存儲的最小單元。

Region由一個或者多個Store組成,每個store保存一個columns

family,每個Strore又由一個memStore和0至多個StoreFile 組成。memStore存儲在內存中, StoreFile存儲在HDFS上。

HBase通過將region切分在許多機器上實現分布式。也就是說,你如果有16GB的數據,只分了2個region, 你卻有20臺機器,有18臺就浪費了。

數目太多就會造成性能下降,現在比以前好多了。但是對于同樣大小的數據,300個region比1000個要好。

數目太少就會妨礙可擴展性,降低并行能力。有的時候導致壓力不夠分散。這就是為什么,你向一個10節點的HBase集群導入200MB的數據,大部分的節點是idle的。

RegionServer中1個region和10個region索引需要的內存量沒有太多的差別。

關于family和column

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

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

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

結合車聯網業務,family的設計一般按照不同的業務數據和獲取頻次進行設計,總的family個數不超過3個為最好,同時將經常讀取的數據歸類到一個family。

關于rowkey

rowkey是hbase的唯一id,也是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列的查詢很慢。

那么rowkey該如何設計?

Hbase是按ASCII碼排序的,其排序規則類似于字典序,例如一個AAA_BBB_CCC的rowkey可以支持以AAA,AAA_BBB,AAA_BBB_CCC的范圍進行查詢,但是不能以AAA__CCC或者_*_CCC這樣的順序進行查詢(*代表缺失)。

結合我們的場景,如果要查詢一個用戶一段時間內的數據,其rowkey應該這樣設計,即USER_(Long.Max-TIME)_EVENT,某個USER的數據會按時間倒序插入hbase,最新的數據會排在最前面,能很容易的找到最新的數據,rowkey上的EVENT是為了避免同一時間會有不同的事件上報被覆蓋,但EVENT不能用于rowkey查詢,因為TIME是在不斷變化的。

這種rowkey的設計其查詢方式可以有:

(1)? 查詢USER的所有數據,即rowkey范圍為{USER_,USER`}

(2)? 查詢USER一段時間內的數據,即rowkey范圍為{USER_( Long.Max-ENDTIME), USER_( Long.Max-STARTRIME)

(3)? 查詢USER一段時間內的符合條件的數據,rowkey按照(2)的方式生成,結合filter

region預分區和寫熱點

上面提到rowkey設計,我們把USER放在rowkey的首位,假設采用hbase默認的策略,即建表初始化一個region,默認達到10G進行拆分,在region分裂之后,不同的USER就會分別寫到不同的region里面去,達到并行寫的目的。但是如果采用(Long.Max-TIME)_USER_EVENT的rowkey方式,region拆分后,后續的數據由于時間越來越來,(Long.Max-TIME)越來越小,數據就只會寫到范圍小的region里面,出現寫熱點問題,所以必須合理的設計rowkey以達到提高寫性能。

Hbase默認是一個表一個region,在region未拆分之前,所有的數據都會往一個region里面寫,即使第一次拆分之后也還是只有2個region,這樣會導致region分布不均,有些節點沒有工作,浪費資源。這時候我們需要考慮region預分區,即一開始就創建足夠的region,每個region劃分一個rowkey范圍。一般我們會按照10進制00000000-FFFFFFFF劃分16個region,如果服務器較少,可以自己制定預分區策略。

Hbase1.x之后,默認的region拆分策略是按照region大小拆分的,早期版本是按照128*(2的n次方)進行拆分的,region拆分的太多不利于regionserver管理,拆的太少不利于數據寫入,具體要根據業務量來制定。

關于filter

顧名思義,filter就是過濾器,filter的作用域是單個region,也就是說filter會在每個region上面獨立生效,當一個用戶的數據跨了幾個region之后,而查詢的范圍又包含這幾個region,如果使用pagefilter分頁,就會返回region個數*分頁條數的數據量。

Filter可以減少io開銷,返回我們所需要的數據,但是如果和scan結合使用需要指定rowkey范圍,scan是掃描表,沒有rowkey范圍就會掃描所有的region,性能非常差。

關于二級索引

由于hbase的rowkey有一定的局限,當查詢條件不在rowkey上,或者不是按照rowkey的組裝順序時,無法通過rowkey來快速找到對應的region和Strore。這時候需要考慮二級索引,二級索引的方式有多種,以下介紹2種方式:

(1)? 通過多張hbase表,一張表存數據,另一張表建立索引和數據表進行關聯,寫數據的時候同時寫兩張表。

(2)? 通過協處理器在put之前建立二級索引,同時寫索引表。

(3)? 結合elasticsearch,hbase存原始數據,elasticsearch建立索引。

關于protobuf

? 在實際業務中,數據通常會比較復雜,一個用戶或者設備會有各種嵌套的屬性,數據在寫入hbase的時候可以以json的方式,也可以以protobuf的方式,這里建議以protobuf的方式,protobuf在序列化和反序列化性能方面比json效率高,同時占用空間也比json要少。

應用實踐規范和注意事項

設計合理的表名,表名不要太長,自注釋的表名最好。

創建表時指定壓縮方式,建議采用LZO,snappy壓縮。

合理安排family,個數不要超過3個,訪問頻繁和訪問關聯的數據放在相同的family中。

合理的設計rowkey,避免以自增長的數據作為rowkey開頭,如果需要,可以hash之后再保存,但hash之后rowkey的順序(比如時間序)已經無法保證了。

盡量簡化封裝,避免使用反射等操作,采用原生api最佳。

數據結構為一對多場景,即一個user對應多個屬性,采用protobuf進行編碼,將多個信號序列化為一個字段。

Hbase連接采用長連接方式。

由于數據上報頻次高,數據量大,采用批量異步保存方式進行保存。利用kafka的特性,一次性拉取一定條數的數據,將這一批數據提交到線程池,批量保存到hbase。

注意不同的hbase版本,zookpeer中的hbase的根節點不同(原生的hbase節點為/hbase,hdp中為/hbase_unsecure)。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。