Hbase的邏輯結(jié)構(gòu)
Hbase物理存儲結(jié)構(gòu)
不同列族分別存在不同的文件夾里寞酿。
數(shù)據(jù)模型
與MySQL比較
- MySQL: DB->Table->列->數(shù)據(jù)
- Hbase: NameSpace->Region(一個表可能對應(yīng)多個region)->列族->數(shù)據(jù)(列+value),列也算是數(shù)據(jù)酒朵。
首先Hbase是依賴于HDFS和zookeeper的厂庇。
Zookeeper分擔(dān)了Hmaster的一部分功能,客戶端進(jìn)行DML語句的時候青灼,都是先跟ZK交互徒役。
RegionServer管理了很多的Region(表),RegionServer里面的WAL(HLog)是預(yù)寫入日志田巴,功能是防止內(nèi)存中的數(shù)據(jù)沒有來的及落盤時丟失钠糊。在Region里面管理的Store管理的是列族,Store里面有Mem Store(內(nèi)存)壹哺,F(xiàn)lush之后抄伍,刪除內(nèi)存中的數(shù)據(jù),同時寫入文件StoreFile Hfile,Hfile 其實是在DataNode里面的管宵。
寫數(shù)據(jù)流程
Hbase的讀比寫慢截珍。
Hbase命名空間下有一張元數(shù)據(jù)表meta表和namespace表。meta表里面保存了要操作的表所在的位置等元數(shù)據(jù)箩朴。
(1)首先客戶端向zk請求元數(shù)據(jù)表所在的RegionServer岗喉,zk返回給客戶端meta表所在的regionServer。
(2)然后客戶端再去對應(yīng)的RegionServer查找meta表炸庞,找到真正要操作的表所在的regionServer钱床,同時把meta表的信息緩存下來,加快后續(xù)的查詢埠居。
(3)然后客戶端再向目標(biāo)表所在的RegionServer發(fā)送put請求诞丽。先把數(shù)據(jù)寫到Hlog里面,再寫到內(nèi)存MemStore,數(shù)據(jù)會在內(nèi)存排序拐格,然后向客戶端發(fā)送ack,到這里對于客戶端來說寫數(shù)據(jù)已經(jīng)結(jié)束了刑赶。再等到MemStore的刷寫時機(jī)后捏浊,將數(shù)據(jù)刷寫到Hfile.
注:meta表所在的位置信息保存在zk的meta-region-server節(jié)點上,客戶端首先就是在這個節(jié)點上差詢meta表所在的RegionServer撞叨。meta表里面的信息就是表與其對應(yīng)的RegionServer的信息
這個stu表可能不止一條金踪,因為stu表可能數(shù)據(jù)量大了之后根據(jù)RowKey進(jìn)行了切分,并且可能會在不同的機(jī)器上牵敷。
Hbase客戶端寫數(shù)據(jù)的源碼的過程胡岔,(1)也要先獲取lock(JUC的鎖)(2)更新時間戳(服務(wù)端的時間)(3)構(gòu)建WAL(還沒有寫到HDFS)(4)操作追加到WAL日志里面,不要同步(5)寫到內(nèi)存里面(6)釋放鎖(7)同步WAL到HDFS(8)如果WAL同步失敗了枷餐,置doRollBackMemStore為true靶瘸,回滾,從內(nèi)存里刪除keys
Flush流程
不同的列族是在不同的文件夾毛肋。
MemStore刷寫時機(jī):
全局的MemStore的容量怨咪,默認(rèn)是堆內(nèi)存的40%。這個容量值會觸發(fā)flush操作润匙,所有的MemStore都要刷寫诗眨,flush操作會阻塞讀寫操作。
會刷寫并阻塞到到MemStore大小降到它的最大容量的95%
內(nèi)存中的文件在自動刷寫之前能夠存活的最長時間孕讳,默認(rèn)1h,按最后一次操作算匠楚。過了1h還沒有到容量巍膘,則自動刷寫。
設(shè)置單個Region的MemStore的容量芋簿,如果超過這單個Region就會刷寫峡懈,默認(rèn)是128M.
WAL日志的刷寫時機(jī):
可以設(shè)置日志的大小和數(shù)量,當(dāng)達(dá)到一定數(shù)量益咬,刷寫到HDFS
Hbase讀流程
(1)從zk找meta表所在的RegionServer
(2)從上述RegionServer里的meta表里找目標(biāo)表所在的RegionServer逮诲,同時把meta表緩存,加速后面的查詢幽告。
(3)向目標(biāo)表所在的RegionServer發(fā)送get請求梅鹦。可以從block Cache冗锁,MemStore還有StoreFile里面查齐唆,具體從哪查根據(jù)時間戳,查時間戳大的冻河,具體就都查然后merge取最新箍邮。
RegionServer里面有block Cache可以緩存磁盤的數(shù)據(jù),加速查詢叨叙。如果block Cache里面有锭弊,就將緩存和MemStore的數(shù)據(jù)merge然后取最新時間戳,沒有就是把磁盤讀的和MemStore里面的合并擂错。所以hbase大多數(shù)讀要走磁盤味滞,所以讀很慢。
StoreFile合并Compaction
每次刷寫會生成新的Hfile钮呀,Hfile很小并且數(shù)量多的時候會影響查詢的速度剑鞍。所以要進(jìn)行合并。合并分為minor Compaction和major Compaction
minor Compaction將臨近的若干較小的Hfile合并成一個較大的Hfile爽醋,不會清理過期和刪除的數(shù)據(jù)蚁署,major Compaction會將一個Store里面的所有Hfile合并成一個大的Hfile,并且會清理掉過期和刪除的數(shù)據(jù)蚂四。
compaction操作需要rewrite光戈,非常消耗資源,一般在業(yè)務(wù)低峰期手動執(zhí)行遂赠。
讀寫擴(kuò)展
數(shù)據(jù)的讀寫可以不依賴Hmaster田度,只需要指定zookeeper,但是Hmaster負(fù)責(zé)region調(diào)度的元數(shù)據(jù)
但是DDL語言是要有Hmaster的
什么時候會觸發(fā)數(shù)據(jù)的刪除
Flush和major Compact
(1)flush在同一個內(nèi)存中清除過期或刪除(刪除標(biāo)記也是一行數(shù)據(jù))的數(shù)據(jù)解愤,但是如果數(shù)據(jù)不同的版本分布在不同的memStroe镇饺,就不能清除。刪除的標(biāo)記在flush之后不會被刪送讲,但在后面的major compaction會把刪除標(biāo)記刪除掉奸笤。
(2)major compaction 會清除過期或刪除的數(shù)據(jù)惋啃。
Region Split
默認(rèn)情況下,每個Table起初只有一個Region监右,隨著數(shù)據(jù)的不斷寫入边灭,Region會自動拆分,兩個子Region開始都會在一個Regionserver里面健盒,但是出于負(fù)載均衡的考慮绒瘦,Hmaster有可能會將某個Region傳給其他的RegionServer。
Split的時機(jī):
(1)當(dāng)一個Region中的某個Store下的StoreFile的總大小查過某個值扣癣,由參數(shù)hbase.hregion.max.filesize設(shè)定(默認(rèn)10g)惰帽,該Region就會按照RowKey進(jìn)行拆分。
(2)在新版本中這個值是Min(R^2*"hbase.hregion.memStore.flush.size(128M)","hbase.hregion.max.filesize"),R是當(dāng)前RegionServer中屬于該Table的Region個數(shù)父虑。分region是按照RowKey切分的该酗。這會導(dǎo)致數(shù)據(jù)傾斜,就是因為切分的閾值在變化士嚎,導(dǎo)致切分之后的region數(shù)據(jù)量不均勻呜魄,導(dǎo)致熱點的問題。所以在建表的時候要做預(yù)分區(qū)莱衩,就是用RowKey規(guī)劃好多少個region爵嗅,不讓hbase自己的切分邏輯切分。
官方建議只用一個列族笨蚁,防止不同的列族之間數(shù)據(jù)不均勻操骡,單一列族數(shù)據(jù)量增多,導(dǎo)致全局的flush赚窃,數(shù)據(jù)量小的列族也要flush,這樣會形成很多小的storeFile岔激。
Hbase API
delete操作:
(1)設(shè)置RowKey:打的刪除標(biāo)記是deleteFamily勒极,刪除多個版本
(2)設(shè)置RowKey+Family:打的標(biāo)記是deleteFamily,刪除多個版本
(3)設(shè)置RowKey+family+column:有addColumn()和addColumns().addColumn是刪除最新的版本或者刪除指定時間戳的版本虑鼎,刪除標(biāo)記是delete標(biāo)記辱匿。addColumns是刪除所有的版本或者刪除指定時間戳或之前的版本,刪除標(biāo)記是deleteColumn
Delete的操作其實也是put操作炫彩,put的是刪除的標(biāo)記匾七。
Hbase優(yōu)化
高可用
在Hbase中HMaster負(fù)責(zé)監(jiān)控HRegionServer的生命周期,均衡RegionServer的負(fù)載江兢,如果HMaster掛掉了昨忆,那個整個Hbase集群將處于不健康的狀態(tài),并且此時的工作狀態(tài)不會維持太久杉允。所以Hbase支持對HMaster的高可用配置邑贴。
在Hbase的conf目錄下新建backup-masters文件席里,vim加入備份Master,比如slave01,slave02.在把文件分發(fā)到各個slave里拢驾,然后再啟動hbase 就能實現(xiàn)HMaster的高可用了奖磁。
預(yù)分區(qū)
每一個region維護(hù)著StartRow和EndRow,如果加入的數(shù)據(jù)符合某個region維護(hù)的RowKey范圍繁疤,則該數(shù)據(jù)交給這個region維護(hù)咖为。那么依照這個原則,我們可以將數(shù)據(jù)所要投放的分區(qū)提前大致的規(guī)劃好稠腊,以提高Hbase性能躁染。
(1)手動設(shè)定預(yù)分區(qū)
create 'staff','info','other',SPLITS =>['1000','2000','3000','4000']
手動設(shè)置RowKey分了5個region
(2)生成16進(jìn)制序列預(yù)分區(qū)
create 'staff1','info','other',{NUMREGIONS=>15,SPLITALGO=>'HexStringSplit'}
(3)按照文件中設(shè)置的規(guī)則預(yù)分區(qū)
創(chuàng)建split.txt
aaaa
bbbb
cccc
dddd
然后執(zhí)行
create 'staff2','info','other',SPLITS_FILE=>'splits.txt'
這里如果文件里面給的分區(qū)鍵不是按照順序的,hbase會先幫我們把鍵排序麻养,然后按照鍵來分區(qū)褐啡。
(4)使用JavaAPI預(yù)分區(qū)
admin的創(chuàng)建表的方法有多個重載,可以只傳表的描述鳖昌,也可以加入分區(qū)的信息备畦。admin.createTable
規(guī)劃分區(qū)要考慮未來數(shù)據(jù)量和機(jī)器的規(guī)模。雖然提前做了分區(qū)许昨,但是最后如果分區(qū)大于了10G,還是會觸發(fā)split懂盐。假設(shè)一臺機(jī)器有100G磁盤,那么預(yù)分區(qū)盡量大于10個糕档,這樣就能避免預(yù)分區(qū)之后又觸發(fā)了大于10G的split莉恼。
RowKey的設(shè)計
(1)希望數(shù)據(jù)能夠盡量均勻的分配在多個分區(qū)里面(散列性)。
(2)唯一性
(3)長度原則(生產(chǎn)環(huán)境70到100位)
常見的設(shè)計方案:
(1)生產(chǎn)隨機(jī)數(shù)速那、hash俐银、散列值
(2)字符串反轉(zhuǎn)
(3)字符串拼接
RowKey設(shè)計案例
電信項目:
一次通話的記錄:13112341233->18998768771 2018-12-12 12:12:21 568
假設(shè)分300個區(qū)
分區(qū)鍵怎么設(shè)計:
(299個鍵)
000|
001|
...
298|
RowKey的前面一般會拼上000_,001_,...,298_
這樣做的好處是,根據(jù)前三位就能知道哪個分區(qū)端仰。
(1)我們希望手機(jī)號盡量分布在不同的分區(qū)捶惜,但是相同的手機(jī)號數(shù)據(jù)集中在同一個分區(qū),這樣方便查詢某個用戶的通話信息荔烧。000_13112341233
(2)因為每個人通話的需求不同吱七,也希望把同一個人的通話記錄也分布在不同的分區(qū)里面。000_13112341233_2019-12-12
哈希取余:[(13112341234^201912).hash]%299
假設(shè)要查詢某用戶2019年2月的通話記錄鹤竭,可以用13112341234201902做startRowkey踊餐,13112341234201903做endRowKey
內(nèi)存優(yōu)化
Hbase實戰(zhàn)
微博。
1臀稚、需求
(1)微博內(nèi)容的瀏覽
(2)用戶社交:關(guān)注用戶吝岭,取關(guān)用戶
(3)拉取關(guān)注人的微博用戶
2、設(shè)計表
(1)微博內(nèi)容表Content
行鍵:用戶id+時間戳
(2)用戶關(guān)系表
因為正常情況一個用戶的粉絲和關(guān)注都不多,可以用一行存儲關(guān)注和粉絲的情況苍碟。
行鍵:用戶id
(3)初始化頁面的表(顯示關(guān)注的人的最近三條微博)