先放一張自己總結(jié)的圖
數(shù)據(jù)模型
- 一個(gè)RegionServer會(huì)管理多個(gè)Region侦副,一個(gè)表的一段鍵值會(huì)生成一個(gè)Region,個(gè)別情況一行數(shù)據(jù)太大也會(huì)導(dǎo)致同一段Region根據(jù)列族切分為不同Region扯躺。
- 每個(gè)Region包含多個(gè)Store,一個(gè)列族分為一個(gè)Store
- 一個(gè)Store只有一個(gè)MemStore,和0到多個(gè)HFile
- 一個(gè)Store有多個(gè)HFile,每次Memstore刷寫(xiě)都會(huì)生成一個(gè)HFile
角色
Master
雖說(shuō)是Master派殷,但其實(shí)可以理解為打雜的。負(fù)責(zé)啟動(dòng)的時(shí)候分配Region到具體的RegionServer墓阀,Master不負(fù)責(zé)查詢(xún)相關(guān)愈腾,只負(fù)責(zé)創(chuàng)建表、修改列族以及Region分割岂津、合并虱黄、移動(dòng)并等。所以如果Master宕機(jī)吮成,集群仍能提供服務(wù)橱乱,只是不能做表級(jí)修改操作了辜梳。
RegionServer
負(fù)責(zé)管理一個(gè)或多個(gè)Region,是存放Region的容器泳叠,一般來(lái)說(shuō)一個(gè)服務(wù)器只有一個(gè)RegionServer作瞄,但是也允許啟動(dòng)多個(gè),客戶(hù)端從Zookeeper獲取數(shù)據(jù)的地址后危纫,會(huì)直接從RegionServer讀取數(shù)據(jù)宗挥,不經(jīng)過(guò)Master,數(shù)據(jù)的插入刪除也是直接在RegionServer上進(jìn)行的种蝶。
Region
表的一部分?jǐn)?shù)據(jù)契耿,相當(dāng)于關(guān)系型數(shù)據(jù)庫(kù)中的一個(gè)分區(qū),Region不能跨服務(wù)器螃征,每個(gè)Region包含起始rowkey和結(jié)束rowkey來(lái)確定其存儲(chǔ)的數(shù)據(jù)范圍
Zookeeper
雖然是第三方的組件搪桂,但是其重要性要超過(guò)Master。讀取數(shù)據(jù)的元數(shù)據(jù)表hbase:meata的位置存儲(chǔ)在Zookeeper上盯滚,沒(méi)有Zookeeper就什么都做不了踢械。Zookeeper負(fù)責(zé)監(jiān)控RegionServer的活性扇谣,如果RegionServer死了哑蔫,Zookeeper檢測(cè)到后會(huì)通知Master將數(shù)據(jù)遷移到其他RegionServer衔瓮。
預(yù)寫(xiě)日志W(wǎng)AL
Write-AHead Log催蝗,用來(lái)解決宕機(jī)之后的操作恢復(fù)問(wèn)題毒返。當(dāng)操作到達(dá)Region的時(shí)候细层,HBase先將數(shù)據(jù)寫(xiě)入到WAL中辕录,保存在HDFS的/hbase/.logs中玫鸟。之后才會(huì)將數(shù)據(jù)寫(xiě)到Memstore中退渗,如果超時(shí)移稳,那么將其刷寫(xiě)到最終的HFile中,如果過(guò)程數(shù)據(jù)丟失那么就可以通過(guò)WAL來(lái)恢復(fù)会油,WAL中不區(qū)分Store个粱,數(shù)據(jù)不能被直接讀取和使用。
WAL默認(rèn)是開(kāi)啟的翻翩,可以使用下邊的語(yǔ)句關(guān)閉:
Mutation.setDurability(Durability.SKIP_WAL)
Put/Append/Increment/Delete都是Mutation的子類(lèi)都许,都有setDurability。關(guān)閉WAL可以讓數(shù)據(jù)導(dǎo)入更快一些嫂冻,但是一般不建議這么做胶征。不過(guò)有一個(gè)折中方案,一步寫(xiě)入WAL來(lái)實(shí)現(xiàn)提高寫(xiě)入性能桨仿;正常的WAL(同步)都是在數(shù)據(jù)來(lái)到Region時(shí)候先放入內(nèi)存中睛低,這些改動(dòng)會(huì)立刻被寫(xiě)入WAL,就算只有一個(gè)改動(dòng)也會(huì)調(diào)用HDFS接口來(lái)同步數(shù)據(jù)。而異步寫(xiě)入會(huì)等到條件滿(mǎn)足的時(shí)候才寫(xiě)入WAL钱雷,這里主要使用hbase.regionserver.optionallogflushinterval骂铁,也就是每隔多長(zhǎng)時(shí)間將數(shù)據(jù)寫(xiě)入WAL,默認(rèn)1s罩抗。設(shè)置方式也是setDurability:
Mutation.setDurability(Durability.ASYNC_WAL)
但是異步寫(xiě)入滅有事務(wù)保證拉庵,異步數(shù)據(jù)及時(shí)寫(xiě)入成功,失敗的時(shí)候也會(huì)丟失套蒂,所以除非對(duì)系統(tǒng)性能要求極高钞支,對(duì)數(shù)據(jù)一致性要求不高,并且系統(tǒng)的性能總是出現(xiàn)在WAL上的時(shí)候才需要考慮異步寫(xiě)入操刀。
預(yù)寫(xiě)日志滾動(dòng)機(jī)制
WAL是一個(gè)環(huán)狀滾動(dòng)日志結(jié)構(gòu)烁挟。數(shù)據(jù)放入WAL時(shí)候會(huì)被寫(xiě)入HDFS的/hbase/.logs目錄下,而超過(guò)檢查時(shí)間并且數(shù)據(jù)已經(jīng)持久化了馍刮,那么就會(huì)移動(dòng)到/hbase/.oldlogs目錄下信夫,這個(gè)過(guò)程叫滾動(dòng)窃蹋。之后如果超過(guò)TTL時(shí)間卡啰,或者不需要作為恢復(fù)數(shù)據(jù)的備份了,那么數(shù)據(jù)會(huì)從/hbase.oldlogs中刪除警没,至此一份數(shù)據(jù)在WAL的旅程就走完了匈辱。觸發(fā)滾動(dòng)的條件有:
- 每隔hbase.regionserver.logroll.period時(shí)間,檢查WAL中數(shù)據(jù)是否已經(jīng)被持久化到HDFS上了
- WAL所在的Block塊快要滿(mǎn)了
- WAL所占的空間大于等于閾值:hbase.regionserver.hlog.blocksize * hbase.regionserver.logroll.multiplier(默認(rèn)0.95)
影響WAL文件從/hbase/.oldlogs完全刪除的條件有:
- TTL進(jìn)程:該進(jìn)程保證WAL文件一致存貨到hbase.master.logcleaner.ttl定義的超時(shí)時(shí)間
- replication被份機(jī)制:如果開(kāi)啟HBase備份機(jī)制杀迹,要保證備份集群已經(jīng)不需要該WAL了亡脸。
MemStore
這個(gè)過(guò)程相當(dāng)于我們?cè)诖驌淇说臅r(shí)候,抽牌之后在手上對(duì)牌進(jìn)行整理的過(guò)程树酪。有時(shí)候我們會(huì)想浅碾,既然WAL已經(jīng)寫(xiě)到HDFS上了,為什么還要再放入MemStore中呢续语。這是因?yàn)镠DFS是只允許數(shù)據(jù)寫(xiě)入和追加垂谢,而不允許數(shù)據(jù)修改,Hbase為了高性能需要保證一個(gè)Region的數(shù)據(jù)是按照數(shù)據(jù)順序存放的疮茄。而MemStore的意義就在于現(xiàn)在內(nèi)存中實(shí)現(xiàn)數(shù)據(jù)排序之后在分別寫(xiě)入對(duì)應(yīng)的數(shù)據(jù)位置滥朱。每個(gè)Store只有一個(gè)MemStore,MemStore內(nèi)部先將數(shù)據(jù)整理為L(zhǎng)SM樹(shù)結(jié)構(gòu)力试,然后再刷寫(xiě)到HFile中徙邻。所以區(qū)別于正常的理解,Memstore存入內(nèi)存并不是為了寫(xiě)入快畸裳,而且就算增加Memstore大小也不能加快寫(xiě)入速度缰犁,Memstore的意義是維持?jǐn)?shù)據(jù)按照rowkey順序排列而不是做一個(gè)緩存。緩存有專(zhuān)門(mén)的BlockCache來(lái)實(shí)現(xiàn)。
另外帅容,Memstore可以?xún)?yōu)化數(shù)據(jù)的存儲(chǔ)薇芝,比如有些數(shù)據(jù)在插入之后馬上刪除了,刷寫(xiě)的時(shí)候就可以直接跳過(guò)該數(shù)據(jù)丰嘉。LSM樹(shù)是Google BigTable和Hbase的基本存儲(chǔ)算法夯到,注重的是在頻繁的數(shù)據(jù)改動(dòng)下保持系統(tǒng)讀取速度的穩(wěn)定性,算法的核心是盡量保證數(shù)據(jù)順序存儲(chǔ)到磁盤(pán)上饮亏。
當(dāng)MemStore太大達(dá)到了閥值耍贾,或者達(dá)到了耍些時(shí)間間隔閥值,會(huì)將內(nèi)容刷寫(xiě)為HFile路幸。
HFile
MemStore的每一次刷寫(xiě)都會(huì)生成一個(gè)HFile荐开,很多人管HFile叫做StoreFile,我們可以理解StoreFile就是HFile的抽象類(lèi)简肴。HFile是由一個(gè)個(gè)塊組成的晃听,在HBase中一個(gè)塊的默認(rèn)為64k,由列族上的BlockSize屬性定義砰识。HFile存儲(chǔ)的數(shù)據(jù)如圖:
這些塊區(qū)分了不同的角色能扒,有的塊只負(fù)責(zé)存儲(chǔ)目標(biāo)塊索引信息,也就是指定塊的偏移值(offset):
- Trailer:必選辫狼,存儲(chǔ)FileInfo初斑、DataIndex、MetaIndex
- MetaIndex:可選膨处,存儲(chǔ)Meta塊索引信息见秤,有Meta塊才會(huì)有MetaIndex
- DataIndex:可選,存儲(chǔ)Data塊索引信息真椿,有Data塊才會(huì)有DataIndex
- FileInfo:必選鹃答,文件信息,其實(shí)也是數(shù)據(jù)存儲(chǔ)塊突硝,存儲(chǔ)當(dāng)前文件的信息测摔,比如最后一個(gè)Last Key,平均Key的長(zhǎng)度(Avg Key Len)等狞换,只有在文件關(guān)閉的時(shí)候才會(huì)寫(xiě)入
- Meta:可選避咆,元數(shù)據(jù)塊,也是只有在文件關(guān)閉的時(shí)候才會(huì)寫(xiě)入修噪,Meta塊存儲(chǔ)了該Hfile文件的元數(shù)據(jù)信息
- Data:可選查库,數(shù)據(jù)塊,HBase的數(shù)據(jù)就存放在這里黄琼,雖然是可選樊销,但是很難看到?jīng)]有Data塊的HFile
Data塊
上述HFile中最重要的數(shù)據(jù)存儲(chǔ)位置-Data塊整慎,其實(shí)內(nèi)部也非常復(fù)雜:
Data塊會(huì)在第一位存儲(chǔ)塊的類(lèi)型,后邊存儲(chǔ)的是多個(gè)keyValue鍵值對(duì)围苫,也就是單元格Cell的實(shí)現(xiàn)類(lèi)裤园。Cell是一個(gè)接口,keyValue是它的實(shí)現(xiàn)類(lèi)剂府。
BlockType(塊類(lèi)型)隨著Hbase的發(fā)展一直在增加拧揽,到目前有以下幾種:
- DATA
- ENCODED_DATA
- LEAF_INDEX
- BLOOM_CHUNK
- META
- INTERMEDIATE_INDEX
- ROOT_INDEX
- FILE_INFO
- GENERAL_BOOLM_META
- DELETE_FAMILY_BLOOM_META
- TRAILER
- INDEX_V1
KeyValue類(lèi)(Cell單元)
一個(gè)KeyValue類(lèi)最后一個(gè)部分是存儲(chǔ)的Value,前面的部分都是存儲(chǔ)和該單元格相關(guān)的元數(shù)據(jù)信息腺占,所以會(huì)導(dǎo)致有時(shí)候value很小淤袜,那么單元格大部分空間存儲(chǔ)元數(shù)據(jù)的情況。
因?yàn)镠DFS是不可修改的衰伯,那么HBase是怎么實(shí)現(xiàn)怎刪改查的呢铡羡?其實(shí)都是基于增加記錄實(shí)現(xiàn)的,修改數(shù)據(jù)的的時(shí)候意鲸,增加一個(gè)記錄烦周,只是版本號(hào)比之前的大而已。當(dāng)需要?jiǎng)h除一個(gè)數(shù)據(jù)的時(shí)候怎顾,依舊會(huì)在增加一個(gè)記錄读慎,但是沒(méi)有value值,他的類(lèi)型為Delete杆勇,被叫做墓碑標(biāo)記(Tombstone)贪壳,在major compaction會(huì)完全刪除饱亿。
查詢(xún)定位
讀取數(shù)據(jù)的時(shí)候優(yōu)先從BlocakCache中找蚜退,如果沒(méi)有再去Memstore和HFile,而不是我們理解的上來(lái)直接尋找Memstore彪笼。另一個(gè)與我們常規(guī)認(rèn)知不同的是钻注,san操作在掃描到包含的數(shù)據(jù)依舊會(huì)繼續(xù)進(jìn)行掃描,因?yàn)橛行?shù)據(jù)的墓碑標(biāo)記是很之前版本分開(kāi)存放的配猫,至少掃描出的數(shù)據(jù)大于給定的條件為止幅恋,這樣它才知道應(yīng)該返回哪些數(shù)據(jù),所以過(guò)濾條件的使用并不能加快san速度泵肄,只有縮小行鍵范圍才能明顯加快掃描速度捆交。另外,scan的過(guò)程中腐巢,store會(huì)創(chuàng)建StoreScanner實(shí)例品追,會(huì)把Memstore和HFile結(jié)合起來(lái)掃描,所以并不是所說(shuō)的先掃M(jìn)emstore后掃HFile冯丙。
Region定位
早期(0.96.0)版本之前是三層查詢(xún)架構(gòu)肉瓦,這個(gè)只做了解。-ROOT- => .META. => Region 這種結(jié)構(gòu)步驟為:
- 從Zookeeper查詢(xún)/hbase/root-region-server節(jié)點(diǎn)查詢(xún)-ROOT-表的RegionServer位置
- 訪問(wèn)-ROOT-表得到.META的RegionServer位置
- 訪問(wèn).META.表得到所需行鍵的Region范圍
- 得到具體數(shù)據(jù)的RegionServer范圍,之后Scan
后來(lái)發(fā)現(xiàn)泞莉,上述的架構(gòu)可以容納多大17億個(gè)Region哪雕,但實(shí)際上不可能用這么多。并且設(shè)計(jì)上允許多個(gè).META表存在鲫趁,但發(fā)現(xiàn).META.一直只有一個(gè)斯嚎,-ROOT-只是存了一行數(shù)據(jù),形同虛設(shè)挨厚,并且三層架構(gòu)較復(fù)雜孝扛,bug可能性更高。
后來(lái)改為了兩層架構(gòu)hbase:meta => Region幽崩。將-ROOT-表去掉了苦始,同時(shí)zk中的/hbase/root-region-server也去掉了,.META.表改為了/hbase/meta-region-server存儲(chǔ)慌申,并把.META.表的名字也修改為hbase:meta了陌选。流程為:
- 客戶(hù)端通過(guò)zk的/hbase/meta-region-server節(jié)點(diǎn)查詢(xún)hbase:meta在哪個(gè)RegionServer上,
- 客戶(hù)端直接訪問(wèn)RegionServer的hbase:meta表蹄溉,查詢(xún)r(jià)owkey的Region范圍咨油,以及RegionServer是哪個(gè)
- 客戶(hù)端連接RegionServer獲取rowkey
- 一般會(huì)將meta信息緩存,下次操作就可以直接從第二步開(kāi)始了柒爵,不需要訪問(wèn)zk了役电。