???????????????????????????????????? Hbase生產(chǎn)實踐
HBase是一個分布式的、面向列的開源數(shù)據(jù)庫偏形,它是hadoop生態(tài)圈的一員但指,有海量數(shù)據(jù)存儲能力寡痰,對資源的消耗也相對較小,但同時查詢能力也有局限棋凳,因此如何正確的使用hbase非常關(guān)鍵拦坠。
一個表可以有上億行,上百萬列
面向列(族)的存儲和權(quán)限控制剩岳,列(族)獨立檢索贞滨。
對于為空(null)的列,并不占用存儲空間拍棕,因此晓铆,表可以設(shè)計的非常稀疏。
選擇完成時绰播,被選擇的列要重新組裝
INSERT/UPDATE比較麻煩
Region是HBase中分布式存儲和負載均衡的最小單元骄噪。不同Region分布到不同RegionServer上,但并不是存儲的最小單元蠢箩。
Region由一個或者多個Store組成链蕊,每個store保存一個columns
family事甜,每個Strore又由一個memStore和0至多個StoreFile 組成。memStore存儲在內(nèi)存中滔韵, StoreFile存儲在HDFS上讳侨。
HBase通過將region切分在許多機器上實現(xiàn)分布式。也就是說奏属,你如果有16GB的數(shù)據(jù),只分了2個region潮峦, 你卻有20臺機器囱皿,有18臺就浪費了。
數(shù)目太多就會造成性能下降忱嘹,現(xiàn)在比以前好多了嘱腥。但是對于同樣大小的數(shù)據(jù),300個region比1000個要好拘悦。
數(shù)目太少就會妨礙可擴展性齿兔,降低并行能力。有的時候?qū)е聣毫Σ粔蚍稚⒋∶住_@就是為什么分苇,你向一個10節(jié)點的HBase集群導(dǎo)入200MB的數(shù)據(jù),大部分的節(jié)點是idle的屁桑。
RegionServer中1個region和10個region索引需要的內(nèi)存量沒有太多的差別医寿。
它是column的集合,在創(chuàng)建表的時候就指定蘑斧,不能頻繁修改靖秩。值得注意的是,列族的數(shù)量越少越好竖瘾,因為過多的列族相互之間會影響沟突,生產(chǎn)環(huán)境中的列族一般是一個到兩個。
和列族的限制數(shù)量不同捕传,列族可以包含很多個列惠拭,前面說的“幾十億行*百萬列”就是這個意思。
存在單元格(cell)中庸论。每一列的值允許有多個版本求橄,由timestamp來區(qū)分不同版本。多個版本產(chǎn)生原因:向同一行下面的同一個列多次插入數(shù)據(jù)葡公,每插入一次就有一個對應(yīng)版本的value罐农。
結(jié)合車聯(lián)網(wǎng)業(yè)務(wù),family的設(shè)計一般按照不同的業(yè)務(wù)數(shù)據(jù)和獲取頻次進行設(shè)計催什,總的family個數(shù)不超過3個為最好涵亏,同時將經(jīng)常讀取的數(shù)據(jù)歸類到一個family。
rowkey是hbase的唯一id,也是hbase查詢的主要途徑气筋,既然HBase是采用KeyValue的列存儲拆内,那Rowkey就是KeyValue的Key了,表示唯一一行宠默。Rowkey也是一段二進制碼流麸恍,最大長度為64KB,內(nèi)容可以由使用的用戶自定義搀矫。數(shù)據(jù)加載時抹沪,一般也是根據(jù)Rowkey的二進制序由小到大進行的。
HBase是根據(jù)Rowkey來進行檢索的瓤球,系統(tǒng)通過找到某個Rowkey (或者某個 Rowkey 范圍)所在的Region融欧,然后將查詢數(shù)據(jù)的請求路由到該Region獲取數(shù)據(jù)。HBase的檢索支持3種方式:
(1) 通過單個Rowkey訪問卦羡,即按照某個Rowkey鍵值進行g(shù)et操作噪馏,這樣獲取唯一一條記錄;
(2) 通過Rowkey的range進行scan绿饵,即通過設(shè)置startRowKey和endRowKey欠肾,在這個范圍內(nèi)進行掃描。這樣可以按指定的條件獲取一批記錄拟赊;
(3) 全表掃描董济,即直接掃描整張表中所有行記錄。
HBASE按單個Rowkey檢索的效率是很高的要门,耗時在1毫秒以下虏肾,每秒鐘可獲取1000~2000條記錄,不過非key列的查詢很慢欢搜。
那么rowkey該如何設(shè)計封豪?
Hbase是按ASCII碼排序的,其排序規(guī)則類似于字典序炒瘟,例如一個AAA_BBB_CCC的rowkey可以支持以AAA吹埠,AAA_BBB,AAA_BBB_CCC的范圍進行查詢,但是不能以AAA__CCC或者_*_CCC這樣的順序進行查詢(*代表缺失)疮装。
結(jié)合我們的場景缘琅,如果要查詢一個用戶一段時間內(nèi)的數(shù)據(jù),其rowkey應(yīng)該這樣設(shè)計廓推,即USER_(Long.Max-TIME)_EVENT刷袍,某個USER的數(shù)據(jù)會按時間倒序插入hbase,最新的數(shù)據(jù)會排在最前面樊展,能很容易的找到最新的數(shù)據(jù)呻纹,rowkey上的EVENT是為了避免同一時間會有不同的事件上報被覆蓋堆生,但EVENT不能用于rowkey查詢,因為TIME是在不斷變化的雷酪。
這種rowkey的設(shè)計其查詢方式可以有:
(1)? 查詢USER的所有數(shù)據(jù)淑仆,即rowkey范圍為{USER_,USER`}
(2)? 查詢USER一段時間內(nèi)的數(shù)據(jù)哥力,即rowkey范圍為{USER_( Long.Max-ENDTIME), USER_( Long.Max-STARTRIME)
(3)? 查詢USER一段時間內(nèi)的符合條件的數(shù)據(jù)蔗怠,rowkey按照(2)的方式生成,結(jié)合filter
region預(yù)分區(qū)和寫熱點
上面提到rowkey設(shè)計吩跋,我們把USER放在rowkey的首位寞射,假設(shè)采用hbase默認的策略,即建表初始化一個region钞澳,默認達到10G進行拆分,在region分裂之后涨缚,不同的USER就會分別寫到不同的region里面去轧粟,達到并行寫的目的。但是如果采用(Long.Max-TIME)_USER_EVENT的rowkey方式脓魏,region拆分后兰吟,后續(xù)的數(shù)據(jù)由于時間越來越來,(Long.Max-TIME)越來越小茂翔,數(shù)據(jù)就只會寫到范圍小的region里面混蔼,出現(xiàn)寫熱點問題,所以必須合理的設(shè)計rowkey以達到提高寫性能珊燎。
Hbase默認是一個表一個region惭嚣,在region未拆分之前,所有的數(shù)據(jù)都會往一個region里面寫悔政,即使第一次拆分之后也還是只有2個region晚吞,這樣會導(dǎo)致region分布不均,有些節(jié)點沒有工作谋国,浪費資源槽地。這時候我們需要考慮region預(yù)分區(qū),即一開始就創(chuàng)建足夠的region芦瘾,每個region劃分一個rowkey范圍捌蚊。一般我們會按照10進制00000000-FFFFFFFF劃分16個region,如果服務(wù)器較少近弟,可以自己制定預(yù)分區(qū)策略缅糟。
Hbase1.x之后,默認的region拆分策略是按照region大小拆分的祷愉,早期版本是按照128*(2的n次方)進行拆分的溺拱,region拆分的太多不利于regionserver管理逃贝,拆的太少不利于數(shù)據(jù)寫入,具體要根據(jù)業(yè)務(wù)量來制定迫摔。
顧名思義沐扳,filter就是過濾器,filter的作用域是單個region句占,也就是說filter會在每個region上面獨立生效沪摄,當一個用戶的數(shù)據(jù)跨了幾個region之后,而查詢的范圍又包含這幾個region纱烘,如果使用pagefilter分頁杨拐,就會返回region個數(shù)*分頁條數(shù)的數(shù)據(jù)量。
Filter可以減少io開銷擂啥,返回我們所需要的數(shù)據(jù)哄陶,但是如果和scan結(jié)合使用需要指定rowkey范圍,scan是掃描表哺壶,沒有rowkey范圍就會掃描所有的region屋吨,性能非常差。
由于hbase的rowkey有一定的局限山宾,當查詢條件不在rowkey上至扰,或者不是按照rowkey的組裝順序時,無法通過rowkey來快速找到對應(yīng)的region和Strore资锰。這時候需要考慮二級索引敢课,二級索引的方式有多種,以下介紹2種方式:
(1)? 通過多張hbase表绷杜,一張表存數(shù)據(jù)直秆,另一張表建立索引和數(shù)據(jù)表進行關(guān)聯(lián),寫數(shù)據(jù)的時候同時寫兩張表鞭盟。
(2)? 通過協(xié)處理器在put之前建立二級索引切厘,同時寫索引表。
(3)? 結(jié)合elasticsearch懊缺,hbase存原始數(shù)據(jù)疫稿,elasticsearch建立索引。
? 在實際業(yè)務(wù)中鹃两,數(shù)據(jù)通常會比較復(fù)雜遗座,一個用戶或者設(shè)備會有各種嵌套的屬性,數(shù)據(jù)在寫入hbase的時候可以以json的方式俊扳,也可以以protobuf的方式途蒋,這里建議以protobuf的方式,protobuf在序列化和反序列化性能方面比json效率高馋记,同時占用空間也比json要少号坡。
設(shè)計合理的表名懊烤,表名不要太長,自注釋的表名最好宽堆。
創(chuàng)建表時指定壓縮方式腌紧,建議采用LZO,snappy壓縮畜隶。
合理安排family壁肋,個數(shù)不要超過3個,訪問頻繁和訪問關(guān)聯(lián)的數(shù)據(jù)放在相同的family中籽慢。
合理的設(shè)計rowkey浸遗,避免以自增長的數(shù)據(jù)作為rowkey開頭,如果需要箱亿,可以hash之后再保存跛锌,但hash之后rowkey的順序(比如時間序)已經(jīng)無法保證了。
盡量簡化封裝届惋,避免使用反射等操作髓帽,采用原生api最佳。
數(shù)據(jù)結(jié)構(gòu)為一對多場景盼樟,即一個user對應(yīng)多個屬性氢卡,采用protobuf進行編碼锈至,將多個信號序列化為一個字段晨缴。
Hbase連接采用長連接方式。
由于數(shù)據(jù)上報頻次高峡捡,數(shù)據(jù)量大击碗,采用批量異步保存方式進行保存。利用kafka的特性们拙,一次性拉取一定條數(shù)的數(shù)據(jù)稍途,將這一批數(shù)據(jù)提交到線程池,批量保存到hbase砚婆。
注意不同的hbase版本械拍,zookpeer中的hbase的根節(jié)點不同(原生的hbase節(jié)點為/hbase,hdp中為/hbase_unsecure)装盯。