Hbase是什么枫笛?
其源于 Google 三大論文之一的 bigtable ,
是一個具有高可靠性沛厨、高性能宙地、面向列、可伸縮的分布式存儲系統(tǒng)逆皮,
簡單來說就是一個數(shù)據(jù)庫宅粥。
Hbase的應(yīng)用場景
這個問題比較不好回答,我們不如換個角度問:為什么要用 hbase:
- 數(shù)據(jù)量大电谣,傳統(tǒng)關(guān)系型數(shù)據(jù)庫無法滿足我們的需求
- 需要處理高并發(fā)請求
- 對實時性要求比較高
- 使用了HDFS 作為底層數(shù)據(jù)儲存
- 非結(jié)構(gòu)或者半結(jié)構(gòu)化數(shù)據(jù)
以上四點基本就是為什么我要用hbase
基礎(chǔ)概念
基本概念
- Row key
- Column 和 Column Family
- Timestamp
以上三個概念結(jié)合下面這張表結(jié)構(gòu)圖來理解下即可秽梅。
我們可以看到,一個rowkey 對應(yīng)有多個 column family 剿牺,
其中 每個 column family 下又有多個 column企垦,
一個rowkey 和一個 column 會確定一個 cell。
其中 timestamp 圖中沒有體現(xiàn)晒来,
其實在每個cell里面保存了多個版本的數(shù)據(jù)钞诡,
而這個版本就是用 timestamp來做版本號的。
架構(gòu)
官方架構(gòu)圖如下:
- HMaster節(jié)點:
- 管理HRegionServer湃崩,實現(xiàn)其負載均衡荧降。
- 管理和分配HRegion,比如在HRegion split時分配新的HRegion竹习;
在HRegionServer退出時遷移其內(nèi)的HRegion到其他HRegionServer上誊抛。 - 實現(xiàn)DDL操作(Data Definition Language,namespace和table的增刪改整陌,column familiy的增刪改等)拗窃。
- 管理namespace和table的元數(shù)據(jù)(實際存儲在HDFS上)。
- 權(quán)限控制(ACL)泌辫。
- HRegionServer節(jié)點:
- 存放和管理本地HRegion随夸。
- 讀寫HDFS,管理Table中的數(shù)據(jù)震放。
- Client直接通過HRegionServer讀寫數(shù)據(jù)(從HMaster中獲取元數(shù)據(jù)宾毒,找到RowKey所在的HRegion/HRegionServer后)。
- 負責過大 region 的切分
- Region
HBase自動把表水平劃分成多個區(qū)域(region)殿遂,
每個region會保存一個表里面某段連續(xù)的數(shù)據(jù)每個表一開始只有一個region诈铛,
隨著數(shù)據(jù)不斷插入表乙各,region不斷增大,
當增大到一個閥值的時候幢竹,
region就會等分會兩個新的region(裂變)當table中的行不斷增多耳峦,
就會有越來越多的region。
這樣一張完整的表被保存在多個Regionserver 上焕毫。當然蹲坷,生產(chǎn)環(huán)境一般都會有一步預分區(qū)的操作,
能夠有效避免一些無謂的Region拆分發(fā)生邑飒。詳細的自動Region拆分請看: HBase Region 自動拆分策略
- Memstore 與 storefile
Region是水平方向切分的結(jié)果循签,
而Store則是豎直方向切分,
一個store對應(yīng)一個CF(列族)
所以一個region可以有多個store組成疙咸,Store 包括位于 內(nèi)存中的memstore 和 位于HDFS的storefile县匠,
寫操作 先寫入memstore,
當memstore中的數(shù)據(jù)達到某個閾值罕扎,
hregionserver會啟動 flushcache進程寫入storefile聚唐,
每次寫入形成單獨的一個storefile當storefile文件的數(shù)量增長到一定閾值后丐重,
系統(tǒng)會進行合并(minor腔召、major compaction),
在合并過程中會進行版本合并和刪除工作(majar)扮惦,
形成更大的storefile當一個region的某個 storefile的大小超過一定閾值后臀蛛,
會把當前的region的所有StoreFile 均分為兩個,
并由 hmaster 分配到相應(yīng)的regionserver服務(wù)器崖蜜,
實現(xiàn)負載均衡客戶端檢索數(shù)據(jù)浊仆,先在memstore找,
找不到再找storefile豫领。(讀過程最先掃描的blockcache抡柿,圖中未體現(xiàn))
- ZooKeeper集群是協(xié)調(diào)系統(tǒng):
存放整個 HBase 集群的元數(shù)據(jù)以及集群的狀態(tài)信息
(這里強調(diào)一下,是元數(shù)據(jù)狀態(tài)信息等恐,不是元數(shù)據(jù)信息)洲劣。實現(xiàn) HMaster 主從節(jié)點的 failover(容錯)。
-
底層儲存系統(tǒng)
圖中是 hdfs 為儲存系統(tǒng)课蔬,
其實也可以是其他的文件系統(tǒng)囱稽,
比如 Linux 本地文件系統(tǒng)。
Hbase 和底層儲存系統(tǒng)是解耦的二跋,但目前來說战惊,基本都是使用的 hdfs。
Hbas讀寫數(shù)據(jù)簡析
- 讀數(shù)據(jù)
- client 去 zookeeper 集群尋找到 hbass:meta 表所在的 regionserver
- 根據(jù)查找的 rowkey 去 元數(shù)據(jù) 所在的 regionserverA ,尋找 數(shù)據(jù) 所在的 regionserverB
- 根據(jù)查找的 rowkey去數(shù)據(jù)所在的 regionserverB 找到該數(shù)據(jù)進行讀取
具體的流程可以參考https://blog.csdn.net/gingerredjade/article/details/63697830
- 寫數(shù)據(jù)
Client 先訪問 zookeeper扎即,獲取到元數(shù)據(jù)儲存的 regionserver A
通過儲存元數(shù)據(jù)的 regionserver A 吞获,根據(jù) rowkey 找到相對應(yīng)的region况凉,以及管理該 region 的 regionserver B
向 regionserver B 發(fā)起寫入請求
regionserver B 接受到數(shù)據(jù)后,將數(shù)據(jù)保存到 MemStore 各拷,
并根據(jù)相應(yīng)的配置決定是否寫入 HLog文件( 一個標準的Hadoop SequenceFile茎刚,文件中存儲了HLogKey,這些Keys包含了和實際數(shù)據(jù)對應(yīng)的序列號撤逢,主要用于崩潰恢復)同時檢測 MemStore是否達到閾值膛锭,
如果達到了,則flush到磁盤形成 StoreFile 文件這里值得一提的是蚊荣,
Hbase支持更新初狰,刪除 等操作,
但其實質(zhì)都是追加操作互例。
比如:刪除了 A奢入,只是把A標記為刪除,
更新則是追加一個更新的值媳叨,
舊值依然存在腥光,但是只能讀到新值。
只有在發(fā)生 major compact 的時候糊秆,才會對數(shù)據(jù)進行整理武福。不是每次寫操作都會走這個完整的過程,
因為這對zookeeper 和 Hbase的元數(shù)據(jù)節(jié)點 會產(chǎn)生很大的壓力痘番,
所以客戶端是會對元數(shù)據(jù)進行緩存捉片,
只有 無元數(shù)據(jù)或者失效的 情況下會走這個完整的過程,
否則直接去請求對應(yīng)的RegionServer就是了汞舱。
關(guān)于MemoryStore的詳細工作流程可以參照http://www.reibang.com/p/7dbaf45e0d0b
注意:
由于不同的列族會共享region伍纫,所以有可能出現(xiàn),
一個列族已經(jīng)有100萬行昂芜,而另外一個才100行莹规。
當一個要求region分割的時候,
會導致100行的列會同樣分布到多個region中泌神。
所以良漱,一般建議不要設(shè)置多個列族。
不過該問題在2.x版本中有修正腻扇,請參見HBASE-10201/HBASE-3149
-
HLogFile的容錯和恢復
可以參考:https://blog.csdn.net/nigeaoaojiao/article/details/54909921
https://www.cnblogs.com/andy6/p/8978917.html
HLog文件:
就是一個普通的Hadoop Sequence File债热,
Sequence File 的Key是HLogKey對象,
HLogKey中記錄了寫入數(shù)據(jù)的歸屬信息幼苛,
除了table和region名字外窒篱,
同時還包括 sequence number和timestamp,timestamp是“寫入時間”,
sequence number的起始值為0墙杯,或者是最近一次存入文件系統(tǒng)中sequence number配并。
HLog Sequece File的Value是HBase的KeyValue對象,
即對應(yīng)HFile中的KeyValue.容錯和恢復:
每個HRegionServer中都有一個HLog對象高镐,
HLog是一個實現(xiàn)Write Ahead Log的類溉旋,
在每次用戶操作寫入MemStore的同時,
也會寫一份數(shù)據(jù)到HLog文件中嫉髓,
HLog文件定期會滾動出新的Hlog文件观腊,
所以Hlog文件會有很多個,
當一些老舊的Hlog文件失效后(已持久化到StoreFile中的數(shù)據(jù))
就會被RS刪除算行。
當HRegionServer意外終止后梧油,
HMaster會通過Zookeeper感知到,
HMaster首先會處理遺留的 HLog文件州邢,
將其中不同Region的Log數(shù)據(jù)進行拆分儡陨,
分別放到相應(yīng)region的目錄下,
然后再將失效的region重新分配量淌,
領(lǐng)取 到這些region的HRegionServer在Load Region的過程中骗村,
會發(fā)現(xiàn)有歷史HLog需要處理,
因此會Replay HLog中的數(shù)據(jù)到MemStore中呀枢,
然后flush到StoreFiles胚股,完成數(shù)據(jù)恢復。
PS:
本段內(nèi)容準確性有待深入研究
如果我們開啟了異步寫入Hlog硫狞,
Hbase的數(shù)據(jù)寫入 MemoryStore 可能會先于 寫入Hlog信轿,
這很可能會造成一個問題,
寫入 MemoryStore 成功残吩,
客戶端可以讀取到數(shù)據(jù),
但是寫入Hlog失敗了倘核,
這個時候如果在數(shù)據(jù)還沒flush到磁盤就宕機了泣侮,
那么這個數(shù)據(jù)就丟失了,客戶端就讀取不到了紧唱,
這也就發(fā)生了數(shù)據(jù)不一致的情況活尊,
但是其實這是不需要擔心的,
因為如果還沒成功寫入Hlog漏益,這條數(shù)據(jù)客戶端是看不到的蛹锰,
并且如果最后失敗了,那么MemoryStore也會回滾绰疤。
HBase表的設(shè)計(RowKey的設(shè)計)
rowkey的設(shè)計一般都是根據(jù)具體業(yè)務(wù)需求來的铜犬,
總的來說,我們需要根據(jù)rowkey的 字典序排列,
唯一性 特性設(shè)計出 高效讀取 和 負載均衡的 rowkey癣猾,
以下是幾個大概的設(shè)計原則:
- 長度原則:
越小越好敛劝,最長不能超過64kb,一般 10-100個字節(jié)就差不多了 - 個數(shù)原則:
這里是指列簇的個數(shù)纷宇,前面已經(jīng)給出原因夸盟,盡量控制在2個以內(nèi) - 散列原則:
我們知道region是根據(jù)rowkey劃分的,
那么rowkey的散列就可以使得數(shù)據(jù)的負載變得均衡像捶,
一般來說我們可以通過 :- 加隨機前綴
- 加hash值
- rowkey反轉(zhuǎn)
- rowkey唯一原則:必須在設(shè)計上保證其唯一性
- rowkey是按照字典順序排序存儲的上陕,因此,設(shè)計rowkey的時候拓春,要充分利用這個排序的特點唆垃,將經(jīng)常讀取的數(shù)據(jù)存儲到一塊,將最近可能會被訪問的數(shù)據(jù)放到一塊痘儡。 比如:
- 我們需要某個時間端的數(shù)據(jù)進行分析辕万,那么以 timestamp-key 的形式,我們很容易就可以查詢到連續(xù)的時間段 的數(shù)據(jù)沉删。
- 避免熱點問題:上面那個列子渐尿,雖然我們的設(shè)計查詢起來很方便,但是事實上矾瑰,會有嚴重的熱點問題砖茸,所有產(chǎn)生的數(shù)據(jù)都會集中在一個節(jié)點上進行處理,其余的節(jié)點將不會被分配到任何數(shù)據(jù)殴穴,這會導致嚴重的數(shù)據(jù)傾斜凉夯。所以我們也參照 3.散列原則。
- rowkey是按照字典順序排序存儲的上陕,因此,設(shè)計rowkey的時候拓春,要充分利用這個排序的特點唆垃,將經(jīng)常讀取的數(shù)據(jù)存儲到一塊,將最近可能會被訪問的數(shù)據(jù)放到一塊痘儡。 比如:
- 索引表
鑒于上面 3 和 4原則的沖突采幌,
我們就很有必要引入索引表的概念了劲够。
還是以我們需要某個連續(xù)時間端的數(shù)據(jù)進行分析為例:- 滿足 3.散列原則 我們的rowkey可以這么設(shè)計: hash-key_timestamp
- 為了同時滿足 4.唯一原則,
我們可以設(shè)計一個索引表 index_table,
該表的 rowkey 我們可以這么設(shè)計成這樣timestamp_hash-key休傍,
其 對應(yīng)的列的值可以是 hash-key_timestamp征绎,
因為索引表的數(shù)據(jù)很小,
我們完 全是可以不去考慮熱點這個問題磨取,
這樣我們需要 某個時間段的數(shù)據(jù)人柿,
可以通過索引表很快查出相應(yīng)的數(shù)據(jù) 對應(yīng)的 rowkey。
但是這里有個問題是忙厌,我們怎么去建立這個索引表凫岖,
要是hbase的原始數(shù)據(jù)有變動,
我們怎么實時更新索引表呢逢净?以下大概提供幾種思路哥放,
有興趣的請自行了解歼指,或者關(guān)注我后續(xù)的博客:
- 手動處理:通過代碼,每次對hbase的更新婶芭,我們都同時更新一下索引表
- hbase 的協(xié)處理器
當然一般我們都是使用協(xié)處理器來幫助我們處理這個問題东臀,
并且索引表的建立也可以借助第三方,比如:ES
split 和 預分區(qū)
(參考https://www.cnblogs.com/niurougan/p/3976519.html)
什么是 split犀农?
當一個reion達到一定的大小惰赋,為了負載均衡,
我們需要分裂成兩個region呵哨,這個過程就是 split赁濒。hbase是如何處理 split 的?
- 在0.94版本之前 ConstantSizeRegionSplitPolicy 是默認和唯一的split策略孟害。
當某個store(對應(yīng)一個column family)的大小大于配置值hbase.hregion.max.filesize
的時候(默認10G)region就會自動分裂拒炎。 - 而0.94版本中,IncreasingToUpperBoundRegionSplitPolicy 是默認的split策略挨务。
這個策略中击你,最小的分裂大小和region server的region 個數(shù)有關(guān),
當storefile的 size 大于如下公式得出的值的時候就會split谎柄,公式如下:
/**
*R為同一個table中在同一個region server中region的個數(shù)丁侄。
*hbase.hregion.memstore.flush.size 默認值 128MB。
*hbase.hregion.max.filesize默認值為10GB 朝巫。
*/
Min (R^2 * “hbase.hregion.memstore.flush.size”, “hbase.hregion.max.filesize”)
當 R=1 ,那么Min(128MB,10GB)=128MB,也就是說在第一個flush的時候就會觸發(fā)分裂操作鸿摇。
當 R=2 ,的時候Min(22128MB,10GB)=512MB ,當某個store file大小達到512MB的時候,就會觸發(fā)分裂劈猿。
如此類推拙吉,當 R=9 的時候,store file 達到 10GB 的時候就會分裂揪荣,
當R>=9的時候筷黔,store file 達到10GB的時候就會分裂。
- split 點都位于 region 中 rowkey 的中間點,也就是說會盡量將大 region 等分成兩個 小 region变逃。
KeyPrefixRegionSplitPolicy 策略可以保證相同的前綴的row保存在同一個region中必逆。
指定rowkey前綴位數(shù)劃分region,通過讀取 KeyPrefixRegionSplitPolicy.prefix_length 屬性揽乱,該屬性為數(shù)字類型,表示前綴長度粟矿,在進行split時凰棉,按此長度對splitPoint進行截取。此種策略比較適合固定前綴的rowkey陌粹。當table中沒有設(shè)置該屬性撒犀,指定此策略效果等同與使用IncreasingToUpperBoundRegionSplitPolicy。我們可以通過配置 hbase.regionserver.region.split.policy 來指定split策略,我們也可以寫我們自己的split策略或舞。
什么是預分區(qū)荆姆?
也可以叫 pre-split,其實就是預先劃分好 region映凳,
我們知道每個 region 都有一個 startkey 和一個 endkey胆筒,
這樣也就確定了這個 region 所管理的數(shù)據(jù)的范圍,
那么預分區(qū)也就是我們預先將一個個 region 劃分好 startkey 和 endkey诈豌,
確定好各自管理的數(shù)據(jù)仆救。為什么需要預分區(qū)?
默認的 hbase 會有一個region矫渔,
當數(shù)據(jù)越來越大彤蔽,那么這個 region 管理的數(shù)據(jù)越來越多,
當超過閾值庙洼,就會發(fā)生 split 操作顿痪,
而 split 操作會導致我們的hbase有一段不可用的時間,
那么為了盡量規(guī)避這個問題油够,所以我們需要預分區(qū)蚁袭。如何預分區(qū)?
- 首先我們需要對數(shù)據(jù)的分布 和 數(shù)據(jù)量的增長有一個預測
- 根據(jù)數(shù)據(jù)量確定分區(qū)數(shù)叠聋,
然后再對數(shù)據(jù)進行合理的rowkey設(shè)計撕阎,
我們以一個簡單的例子來說明:
假設(shè)我們預測數(shù)據(jù)會有 50G,
那么我們劃分 5 個分區(qū),
分別是:[-∞ - 1)碌补,[1 - 2)虏束,[2 - 3),[3 - 4)厦章,[4 - +∞)镇匀,
rowkey的我們可以設(shè)計成[0-5)的隨機數(shù) + key
- 這樣數(shù)據(jù)就會均勻的分布在我們預先設(shè)計好的分區(qū)上,
也就達到了我們的需求袜啃,
但是需要注意的是汗侵,
隨著數(shù)據(jù)越來越大,
超出我們預估的 50G 那么這個時候群发,
我們也需要重新對分區(qū)進行調(diào)整了
Hbase 數(shù)據(jù)查詢方式
HBase的查詢實現(xiàn)只提供兩種方式:
按指定RowKey 獲取唯一一條記錄晰韵,
get方法(org.apache.hadoop.hbase.client.Get)
Get 的方法處理分兩種 :
設(shè)置了ClosestRowBefore 和沒有設(shè)置的rowlock .
主要是用來保證行的事務(wù)性,
即每個get 是以一個row 來標記的.
一個row中可以有很多 column family 和column.按指定的條件獲取一批記錄熟妓,
scan方法(org.apache.Hadoop.hbase.client.Scan)
實現(xiàn)條件查詢功能使用的就是scan 方式.
scan 可以通過setCaching 與setBatch 方法提高速度(以空間換時間)雪猪;
scan 可以通過setStartRow 與setEndRow 來限定范圍[start,end)
start 是閉區(qū)間起愈,end 是開區(qū)間只恨。
范圍越小推掸,性能越高外臂。scan 可以通過setFilter 方法添加過濾器,
這也是分頁、多條件查詢的基礎(chǔ)呻顽。
compact
在hbase中每當有memstore數(shù)據(jù)flush到磁盤之后叔遂,就形成一個storefile咏连,
當storeFile的數(shù)量達到一定程度后诫舅,
就需要將 storefile 文件來進行 compaction 操作。
Compact 的作用:
- 合并文件
- 清除過期滑绒,多余版本的數(shù)據(jù)
- 提高讀寫數(shù)據(jù)的效率
HBase 中實現(xiàn)了兩種 compaction 的方式:minor and major. 這兩種 compaction 方式的區(qū)別是:
- Minor 操作只用來做部分文件的合并操作以及包括 minVersion=0 并且設(shè)置 ttl 的過
期版本清理闷堡,不做任何刪除數(shù)據(jù)、多版本數(shù)據(jù)的清理工作疑故。 - Major 操作是對 Region 下的HStore下的所有StoreFile執(zhí)行合并操作杠览,最終的結(jié)果是整理合并出一個文件。
宕機處理
宕機分為HMaster宕機和HRegisoner宕機纵势,
- HRegisoner宕機踱阿,HMaster會將其所管理的region重新分布到其他活動的RegionServer上,由于數(shù)據(jù)和日志都持久在HDFS中钦铁,該操作不會導致數(shù)據(jù)丟失软舌。所以數(shù)據(jù)的一致性和安全性是有保障的。
- HMaster宕機牛曹,HMaster沒有單點問題佛点,HBase中可以啟動多個HMaster,通過Zookeeper的Master Election機制保證總有一個Master運行黎比。即ZooKeeper會保證總會有一個HMaster在對外提供服務(wù)超营。
過濾器
可以參考http://www.reibang.com/p/18ef91fbb090
https://www.cnblogs.com/similarface/p/5805973.html
優(yōu)化 (未完)
并發(fā)讀寫
kv 解決key過長的問題:增加value的大小,序列化之后儲存
bulkload
壓縮