HBase读写流程、flush、文件合并、region拆分
HBase存儲(chǔ)原理(架構(gòu))
HBase依賴于Zookeeper和Hadoop的,所以在啟動(dòng)HBase前需要啟動(dòng)Zookeeper和Hadoop。
HMaster用于管理整個(gè)HBase集群,即管理每個(gè)HRegionServer,它掌握著整個(gè)集群的元數(shù)據(jù)信息,同時(shí)會(huì)將相應(yīng)的數(shù)據(jù)存儲(chǔ)到Zookeeper(元數(shù)據(jù)信息、高可用信息等)。
HMaster的職責(zé):
1)管理用戶對(duì)Table的增、刪、改、查操作;
2)記錄region在哪臺(tái) Hregion server上;
3)在Region Split 后,負(fù)責(zé)新Region的分配;
4)新機(jī)器加入時(shí),管理 HRegion Server的負(fù)載均衡,調(diào)整Region分布;
5)在 HRegion Server 宕機(jī)后,負(fù)責(zé)失效 HRegion Server 上的Regions 遷移。
HRegionServer是每臺(tái)機(jī)器上的一個(gè)Java進(jìn)程(一臺(tái)機(jī)器只有一個(gè)HRegionServer進(jìn)程),用來(lái)處理客戶端的讀寫(xiě)請(qǐng)求(和Hadoop的DataNode類(lèi)似),并維護(hù)著元數(shù)據(jù)信息。
HRegionServer的職責(zé):
1) HRegion Server 主要負(fù)責(zé)響應(yīng)用戶I/O請(qǐng)求,向HDFS文件系統(tǒng)中讀寫(xiě)數(shù)據(jù),是 HBASE中最核心的模塊。
2) HRegion Server 管理了很多 table 的分區(qū),也就是region。
每個(gè)HRegionServer有一個(gè)HLog(有且僅有一個(gè))。HLog是操作日志,用來(lái)做災(zāi)難恢復(fù)的,當(dāng)客戶端發(fā)起一個(gè)寫(xiě)請(qǐng)求時(shí),會(huì)先往HLog中寫(xiě)再往Memory Store中寫(xiě)。假設(shè)沒(méi)有HLog,我們進(jìn)行一個(gè)寫(xiě)請(qǐng)求,會(huì)首先寫(xiě)到Memory Store上,等到Memory Store到達(dá)一定容量后,才會(huì)flush到StoreFile中。但是如果在這之前主機(jī)斷電了呢?那這部分操作的數(shù)據(jù)全丟失了。這顯然不是我們想到的結(jié)果,于是就有了HLog。
每個(gè)HRegionServer里面有多個(gè)HRegion,一個(gè)HRegion對(duì)應(yīng)于HBase的一張表(也可能是表的一部分,因?yàn)楸硖罅藭?huì)切分,表和HRegion的對(duì)應(yīng)關(guān)系是一對(duì)多),當(dāng)這張表到一定大小的時(shí)候會(huì)進(jìn)行切分,切分成兩個(gè)HRegion,切分出來(lái)的新的HRegion會(huì)保存到另一臺(tái)機(jī)器上。每個(gè)HRegionServer里面有多個(gè)HRegion,可以理解為有多張表。
每個(gè)HRegion里面有多個(gè)Store(一張表中有多個(gè)列族),一個(gè)Store對(duì)應(yīng)于HBase一張表的一個(gè)列族,。按照這個(gè)原理,我們?cè)谠O(shè)計(jì)列族的時(shí)候,可以把經(jīng)常查詢的列放在同一個(gè)列族,這樣可以提高效率,因?yàn)橥粋€(gè)列族在同一個(gè)文件里面(不考慮切分)。
每個(gè)Store有一個(gè)內(nèi)存級(jí)別的存儲(chǔ)Memory Store(有且僅有一個(gè))。當(dāng)Memory Store達(dá)到一定大小或一定時(shí)間后會(huì)進(jìn)行數(shù)據(jù)刷寫(xiě)(flush),寫(xiě)到磁盤(pán)中(即HFile)。
每個(gè)Store有多個(gè)磁盤(pán)級(jí)別的存儲(chǔ)StoreFile,Memory Store每刷寫(xiě)一次就形成一個(gè)StoreFile,HFile是StoreFile在HDFS上的存儲(chǔ)格式。
HBase讀原理
在上圖中,我們模擬一下客戶端讀取數(shù)據(jù)過(guò)程,假設(shè)Zookeeper存放的meta表在RS1機(jī)器上,meta表存放的內(nèi)容如下,Student表行鍵范圍在1-100的存放在RS4上,在101~200的存放在RS3上,等等。
meta表: _________________________________________________ | 表名 | rowkey范圍 | 所在位置 | |____________|________________|_________________| | Student | 1 ~ 100 | RS4 | |____________|________________|_________________| | Student | 101 ~ 200 | RS3 | |____________|________________|_________________| | Teacher | 1 ~ 500 | RS1 | |____________|________________|_________________| | ··· | |_______________________________________________|客戶端現(xiàn)在要讀取Student表的第100行,具體步驟如下:
客戶端向Zookeeper發(fā)起請(qǐng)求,請(qǐng)求元數(shù)據(jù)所在RegionServer,Zookeeper集群存放的是HBase的meta表所在位置。
Zookeeper返回給客戶端元數(shù)據(jù)所在RegionServer(即RS1)。
客戶端收到應(yīng)答后去請(qǐng)求RS1,請(qǐng)求查詢Student表的rowkey=100數(shù)據(jù)所在位置。
在RS1上查詢meta表可知該數(shù)據(jù)在RS4機(jī)器上,即返回給客戶端rowkey所在位置(RS4)。
客戶端收到應(yīng)答后去請(qǐng)求RS4讀數(shù)據(jù)。
RS4查詢數(shù)據(jù)返回給客戶端。查詢時(shí)先去內(nèi)存(MemStore)查找,因?yàn)閮?nèi)存是最新的數(shù)據(jù),如果找到了就返回結(jié)果,如果沒(méi)找到則去緩存(blockcache(每個(gè)HRegionServer只有一個(gè)))找,如果找到了就返回結(jié)果,如果還沒(méi)找到就去磁盤(pán)(StoreFile)找,如果在磁盤(pán)找到了,則先將結(jié)果寫(xiě)入緩存(blockcache),再返回給客戶端,寫(xiě)入緩存是為了下次查詢提高效率。
注:blockcache中存儲(chǔ)數(shù)據(jù)來(lái)源即Hfile的標(biāo)記,在讀blockcache后,再進(jìn)行讀取未標(biāo)記的Hfile與MemStore進(jìn)行數(shù)據(jù)合并取對(duì)應(yīng)版本數(shù)據(jù),返回并保存至blockcache,再返回至client
在整個(gè)讀過(guò)程中HMaster并沒(méi)有參與,即讀流程與HMaster無(wú)關(guān),所以如果HMaster掛了,也是可以讀數(shù)據(jù)的。
HBase寫(xiě)原理
HBase的寫(xiě)是比讀快的,為什么呢,看下面的寫(xiě)過(guò)程,同樣假設(shè)Zookeeper存放的meta表在RS1機(jī)器上,meta表存放的內(nèi)容如下,Student表行鍵范圍在1100的存放在RS4上,在101200的存放在RS3上,等等。
meta表:
meta表: _________________________________________________ | 表名 | rowkey范圍 | 所在位置 | |____________|________________|_________________| | Student | 1 ~ 100 | RS2 | |____________|________________|_________________| | Student | 101 ~ 200 | RS1 | |____________|________________|_________________| | Teacher | 1 ~ 500 | RS1 | |____________|________________|_________________| | ··· | |_______________________________________________|客戶端現(xiàn)在要插入數(shù)據(jù)給Student表,其中rowkey=100,具體步驟如下:
客戶端向Zookeeper發(fā)起請(qǐng)求,請(qǐng)求元數(shù)據(jù)所在RegionServer,Zookeeper集群存放的是HBase的meta表所在位置。
Zookeeper返回給客戶端元數(shù)據(jù)所在RegionServer(即RS1)。
客戶端收到應(yīng)答后去請(qǐng)求RS1,請(qǐng)求查詢Student表的rowkey=100數(shù)據(jù)所在位置。
在RS1上查詢meta表可知該數(shù)據(jù)在RS2機(jī)器上,即返回給客戶端rowkey所在位置(RS2)。
客戶端收到應(yīng)答后去請(qǐng)求RS4寫(xiě)入數(shù)據(jù)。
RS2收到請(qǐng)求,先將數(shù)據(jù)寫(xiě)入HLog,再將數(shù)據(jù)寫(xiě)入MemStore,寫(xiě)入MemStore后就返回給客戶端寫(xiě)入成功信息,此時(shí),客戶端的寫(xiě)流程完成了。
因?yàn)閷?xiě)入內(nèi)存就結(jié)束了寫(xiě)流程,不用訪問(wèn)磁盤(pán),所以總體比讀流程是快一點(diǎn)的。
同樣,在整個(gè)寫(xiě)流程中HMaster也沒(méi)有參與,所以如果HMaster掛了,也是可以進(jìn)行寫(xiě)數(shù)據(jù)的。但是,如果時(shí)間長(zhǎng)了,表的大小一直變大,而HMaster卻掛了,即不會(huì)觸發(fā)Region切分,這樣就會(huì)導(dǎo)致數(shù)據(jù)傾斜,系統(tǒng)就變得不安全了。
HBase數(shù)據(jù)flush刷寫(xiě)過(guò)程
在hbase-default.xml配置文件中有這么幾項(xiàng)配置(見(jiàn)下面)
1.hbase.hregion.memstore.flush.size
默認(rèn)值:128M
MemStore 級(jí)別限制,當(dāng) Region 中任意一個(gè) MemStore 的大小(壓縮后的大小)達(dá)到了設(shè)定值,會(huì)觸發(fā) MemStore flush。
2.hbase.regionserver.optionalcacheflushinterval
默認(rèn)值:3600000
HBase 定期刷新 MemStore,默認(rèn)周期為1小時(shí),確保 MemStore 不會(huì)長(zhǎng)時(shí)間沒(méi)有持久化。為避免所有的 MemStore 在同一時(shí)間都進(jìn)行 flush,定期的 flush 操作有 20000 左右的隨機(jī)延時(shí)。
3.hbase.hregion.memstore.block.multiplier
默認(rèn)值:2 (3.0版本是4)
Region 級(jí)別限制,當(dāng) Region 中所有 MemStore 的大小總和達(dá)到了設(shè)定值(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默認(rèn) 2* 128M = 256M),會(huì)觸發(fā) MemStore flush,禁止當(dāng)前Region讀寫(xiě)
4.hbase.regionserver.global.memstore.upperLimit
默認(rèn)值:0.4
Region Server 級(jí)別限制,當(dāng)一個(gè) Region Server 中所有 MemStore 的大小總和達(dá)到了設(shè)定值(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默認(rèn) 0.4 * RegionServer堆內(nèi)存大小),會(huì)觸發(fā)全部 MemStore flush,不管MemStore有多小。而且regionserver級(jí)別的flush會(huì)阻塞客戶端讀寫(xiě)。
5.hbase.regionserver.global.memstore.lowerLimit
默認(rèn)值:0.38
與 hbase.regionserver.global.memstore.upperLimit 類(lèi)似,區(qū)別是:當(dāng)一個(gè) Region Server 中所有 MemStore 的大小總和達(dá)到了設(shè)定值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默認(rèn) 0.38 * RS堆內(nèi)存大小),會(huì)觸發(fā)部分 MemStore flush。
Flush 順序是按照 Region 的總 MemStore 大小,由大到小執(zhí)行,先操作 MemStore 最大的 Region,再操作剩余中最大的 Region,直至總體 MemStore 的內(nèi)存使用量低于設(shè)定值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize)。
6.hbase.regionserver.maxlogs
默認(rèn)值:32
當(dāng)一個(gè) Region Server 中 HLog 數(shù)量達(dá)到設(shè)定值,系統(tǒng)會(huì)選取最早的一個(gè) HLog 對(duì)應(yīng)的一個(gè)或多個(gè) Region 進(jìn)行 flush。
當(dāng)增加 MemStore 的大小以及調(diào)整其他的 MemStore 的設(shè)置項(xiàng)時(shí),也需要去調(diào)整 HLog 的配置項(xiàng)。否則,WAL的大小限制可能會(huì)首先被觸發(fā)。因而,將利用不到其他專門(mén)為Memstore而設(shè)計(jì)的優(yōu)化。
需要關(guān)注的 HLog 配置是 HLog 文件大小,由參數(shù) hbase.regionserver.hlog.blocksize 設(shè)置(默認(rèn)512M),HLog 大小達(dá)到上限,或生成一個(gè)新的 HLog
通過(guò)WAL限制來(lái)觸發(fā)Memstore的flush并非最佳方式,這樣做可能會(huì)會(huì)一次flush很多Region,盡管“寫(xiě)數(shù)據(jù)”是很好的分布于整個(gè)集群,進(jìn)而很有可能會(huì)引發(fā)flush“大風(fēng)暴”。
7.手動(dòng)觸發(fā)
用戶可以通過(guò)shell命令一下分別對(duì)一個(gè) Table 或者一個(gè) Region 進(jìn)行 flush:
hbase> flush ‘TABLENAME’
hbase> flush ‘REGIONNAME’
8.其他
執(zhí)行 Compact 和 Split 之前,會(huì)進(jìn)行一次 flush。
需要注意的是HBase的最小flush單元是HRegion而不是單個(gè)MemStore。
Flush是由HMaster觸發(fā)的,Flush順序是按照Memstore由大到小執(zhí)行,先Flush Memstore最大的Region,再執(zhí)行次大的,直至總體Memstore內(nèi)存使用量低于閾值(hbase.regionserver.global.memstore.lowerLimit)。
Flush 阻止更新的情況
1.出現(xiàn)上述(2)的情況,Region 下所有 Memstore 的總大小超過(guò)了 MemStore 默認(rèn)大小的倍數(shù),該 Region 在 flush 完成前會(huì) block 新的更新請(qǐng)求。
2.出現(xiàn)上述(3)的情況,RegionServer 所有 MemStore 占整個(gè)堆的最大比例超過(guò) hbase.regionserver.global.memstore.upperLimit 設(shè)置值,該 RegionServer 的更新請(qǐng)求會(huì)被 block,一直到 MemStore 恢復(fù)閾值一下。
更新被阻塞對(duì)單個(gè)節(jié)點(diǎn)和整個(gè)集群的影響都很大,需要關(guān)注 MemStore 的大小和 Memstore Flush Queue 的長(zhǎng)度。
Memstore Flush 流程
為了減少 flush 過(guò)程對(duì)讀寫(xiě)的影響,HBase 采用了類(lèi)似于兩階段提交的方式,將整個(gè) flush 過(guò)程分為三個(gè)階段:
prepare 階段:遍歷當(dāng)前 Region 中的所有 MemStore,將 MemStore 中當(dāng)前數(shù)據(jù)集 kvset 做一個(gè)快照 snapshot,然后再新建一個(gè)新的 kvset,后期的所有寫(xiě)入操作都會(huì)寫(xiě)入新的 kvset 中。整個(gè) flush 階段讀操作讀 MemStore 的部分,會(huì)分別遍歷新的 kvset 和 snapshot。prepare 階段需要加一把 updateLock 對(duì)寫(xiě)請(qǐng)求阻塞,結(jié)束之后會(huì)釋放該鎖。因?yàn)榇穗A段沒(méi)有任何費(fèi)時(shí)操作,因此持鎖時(shí)間很短。
flush 階段:遍歷所有 MemStore,將 prepare 階段生成的 snapshot 持久化為臨時(shí)文件,臨時(shí)文件會(huì)統(tǒng)一放到目錄.tmp下。這個(gè)過(guò)程因?yàn)樯婕暗酱疟P(pán)IO操作,因此相對(duì)比較耗時(shí)。
commit 階段:遍歷所有的 MemStore,將 flush 階段生成的臨時(shí)文件移到指定的 Column family 目錄下,生成對(duì)應(yīng)的 Storefile(HFile) 和 Reader,把 Storefile 添加到 HStore 的 Storefiles 列表中,最后再清空 prepare 階段生成的 snapshot。
HBase數(shù)據(jù)compaction合并過(guò)程
由于在flush過(guò)程中,可能會(huì)產(chǎn)生很多小文件(這很好理解,比如有兩個(gè)MemStore,一個(gè)很大,一個(gè)很小,然后就觸發(fā)了flush操作,那么那個(gè)小的就形成了小文件),我們都知道,HDFS不適合存儲(chǔ)小文件,所以在寫(xiě)入HDFS之前會(huì)進(jìn)行合并操作。
在hbase-default.xml配置文件中有這么幾項(xiàng)配置:
hbase.hregion.majorcompaction:一個(gè)region進(jìn)行 major compaction合并的周期,在這個(gè)點(diǎn)的時(shí)候, 這個(gè)region下的所有hfile會(huì)進(jìn)行合并,默認(rèn)是7天,major compaction非常耗資源,建議生產(chǎn)關(guān)閉(設(shè)置為0),在應(yīng)用空閑時(shí)間手動(dòng)觸發(fā)。
hbase.hstore.compactionThreshold:一個(gè)store里面允許存的hfile的個(gè)數(shù),超過(guò)這個(gè)個(gè)數(shù)會(huì)被寫(xiě)到新的一個(gè)hfile里面 也即是每個(gè)region的每個(gè)列族對(duì)應(yīng)的memstore在fulsh為hfile的時(shí)候,默認(rèn)情況下當(dāng)超過(guò)3個(gè)hfile的時(shí)候就會(huì)對(duì)這些文件進(jìn)行合并重寫(xiě)為一個(gè)新文件,設(shè)置個(gè)數(shù)越大可以減少觸發(fā)合并的時(shí)間,但是每次合并的時(shí)間就會(huì)越長(zhǎng)。
在我們利用shell命令或者API刪除數(shù)據(jù)的時(shí)候,數(shù)據(jù)并沒(méi)有被刪除,而是被打上標(biāo)記,而是在這里的compaction合并過(guò)程中才會(huì)被完全刪除。
轉(zhuǎn)自:https://blog.csdn.net/hzj1998/article/details/99116931
region拆分
Region自動(dòng)切分是HBase能夠擁有良好擴(kuò)張性的最重要因素之一,也必然是所有分布式系統(tǒng)追求無(wú)限擴(kuò)展性的一副良藥。
Region切分觸發(fā)策略 在最新穩(wěn)定版(1.2.6)中,HBase已經(jīng)有多達(dá)6種切分觸發(fā)策略。當(dāng)然,每種觸發(fā)策略都有各自的適用場(chǎng)景,用戶可以根據(jù)業(yè)務(wù)在表級(jí)別選擇不同的切分觸發(fā)策略。常見(jiàn)的切分策略如下圖:
ConstantSizeRegionSplitPolicy:0.94版本前默認(rèn)切分策略。這是最容易理解但也最容易產(chǎn)生誤解的切分策略,從字面意思來(lái)看,當(dāng)region大小大于某個(gè)閾值(hbase.hregion.max.filesize)之后就會(huì)觸發(fā)切分,實(shí)際上并不是這樣,真正實(shí)現(xiàn)中這個(gè)閾值是對(duì)于某個(gè)store來(lái)說(shuō)的,即一個(gè)region中最大store的大小大于設(shè)置閾值之后才會(huì)觸發(fā)切分。store大小為壓縮后的文件大小(采用壓縮的場(chǎng)景)。
弊 端: 切分策略對(duì)于大表和小表沒(méi)有明顯的區(qū)分。閾值(hbase.hregion.max.filesize)設(shè)置較大對(duì)大表比較友好,但是小表就有可能不會(huì)觸發(fā)分裂,極端情況下可能就1個(gè),這對(duì)業(yè)務(wù)來(lái)說(shuō)并不是什么好事。如果設(shè)置較小則對(duì)小表友好,但一個(gè)大表就會(huì)在整個(gè)集群產(chǎn)生大量的region,這對(duì)于集群的管理、資源使用、failover來(lái)說(shuō)都不是一件好事。
IncreasingToUpperBoundRegionSplitPolicy: 0.94版本~2.0版本默認(rèn)切分策略。這種切分策略微微有些復(fù)雜,總體來(lái)看和ConstantSizeRegionSplitPolicy思路相同,一個(gè)region中最大store大小大于設(shè)置閾值就會(huì)觸發(fā)切分。但是這個(gè)閾值并不像ConstantSizeRegionSplitPolicy是一個(gè)固定的值,而是會(huì)在一定條件下不斷調(diào)整,調(diào)整規(guī)則和region所屬表在當(dāng)前regionserver上的region個(gè)數(shù)有關(guān)系 :(#regions) * (#regions) * (#regions) * flush size * 2,當(dāng)然閾值并不會(huì)無(wú)限增大, 最大值為用戶設(shè)置的MaxRegionFileSize。
優(yōu)點(diǎn): 這種切分策略很好的彌補(bǔ)了ConstantSizeRegionSplitPolicy的短板,能夠自適應(yīng)大表和小表。而且在大集群條件下對(duì)于很多大表來(lái)說(shuō)表現(xiàn)很優(yōu)秀,但并不完美。
弊 端: 這種策略下很多小表會(huì)在大集群中產(chǎn)生大量小region,分散在整個(gè)集群中。而且在發(fā)生region遷移時(shí)也可能會(huì)觸發(fā)region分裂。
SteppingSplitPolicy : 2.0版本默認(rèn)切分策略。這種切分策略的切分閾值又發(fā)生了變化,相比IncreasingToUpperBoundRegionSplitPolicy簡(jiǎn)單了一些,依然和待分裂region所屬表在當(dāng)前regionserver上的region個(gè) 數(shù)有關(guān)系,如果region個(gè)數(shù)等于1,切分閾值為flush size * 2,否則為MaxRegionFileSize。這種切分策略對(duì)于大集群中的大表、小表會(huì)比IncreasingToUpperBoundRegionSplitPolicy更加友好,小表不會(huì)再產(chǎn)生大量的小region,而是適可而止。
另外, 還有一些其他分裂策略, 比如使用DisableSplitPolicy: 可以禁止region 發(fā)生分裂; 而KeyPrefixRegionSplitPolicy ,DelimitedKeyPrefixRegionSplitPolicy 對(duì) 于 切 分 策 略 依 然 依 據(jù) 默 認(rèn) 切 分 策 略 , 但 對(duì) 于 切 分 點(diǎn) 有 自 己 的 看 法 , 比 如KeyPrefixRegionSplitPolicy要求必須讓相同的PrefixKey待在一個(gè)region中。
在用法上,一般情況下使用默認(rèn)切分策略即可,也可以在cf級(jí)別設(shè)置region切分策略,命令為: create’table’{NAME=>‘cf’,SPLIT_POLICY=>‘org.apache.hadoop.hbase.regionserver. ConstantSizeRegionSpli
region切分策略會(huì)觸發(fā)region切分,切分開(kāi)始之后的第一件事就是尋找切分點(diǎn)-splitpoint。所有默認(rèn)切分策略,無(wú)論是ConstantSizeRegionSplitPolicy、IncreasingToUpperBoundRegionSplitPolicy抑或是SteppingSplitPolicy,對(duì)于切分點(diǎn)的定義都是一致的。當(dāng)然,用戶手動(dòng)執(zhí)行切分時(shí)是可以指定切分點(diǎn)進(jìn)行切分的,這里并不討論這種情況。
那切分點(diǎn)是如何定位的呢?整個(gè)region中最大store中的最大文件中最中心的一個(gè)block的首個(gè)rowkey。這是一句比較消耗腦力的語(yǔ)句,需要細(xì)細(xì)品味。另外,HBase還規(guī)定,如果定位到的rowkey是整個(gè)文件的首個(gè)rowkey或者最后一個(gè)rowkey的話,就認(rèn)為沒(méi)有切分點(diǎn)。
什么情況下會(huì)出現(xiàn)沒(méi)有切分點(diǎn)的場(chǎng)景呢?最常見(jiàn)的就是一個(gè)文件只有一個(gè)block,執(zhí)行split的時(shí)候就會(huì)發(fā)現(xiàn)無(wú)法切分。很多新同學(xué) 在測(cè)試split的時(shí)候往往都是新建一張新表,然后往新表中插入幾條數(shù)據(jù)并執(zhí)行一下flush,再執(zhí)行split,奇跡般地發(fā)現(xiàn)數(shù)據(jù)表并沒(méi)有真正執(zhí)行切分。原因就在這里,這個(gè)時(shí)候仔細(xì)的話你翻看debug日志是可以看到這樣的日志滴:
Region 核 心 切 分 流 程
HBase將整個(gè)切分過(guò)程包裝成了一個(gè)事務(wù),意圖能夠保證切分事務(wù)的原子性。整個(gè)分裂事務(wù)過(guò)程分為三個(gè)階段:prepare – execute– (rollback) ,操作模版如下:
prepare階段: 在內(nèi)存中初始化兩個(gè)子region,具體是生成兩個(gè)HRegionInfo對(duì)象,包含tableName、regionName、startkey、endkey等。同時(shí)會(huì)生成一個(gè)transaction journal,這個(gè)對(duì)象用來(lái)記錄切分的進(jìn)展,具體見(jiàn)rollback階段。
execute階段:切分的核心操作。見(jiàn)下圖(來(lái)自Hortonworks):
3.在父存儲(chǔ)目錄下新建臨時(shí)文件夾,split保存split后的daughter region信息。
4.關(guān)閉parent region:parent region 關(guān)閉數(shù)據(jù)寫(xiě)入并觸發(fā)flush操作,將寫(xiě)入region的數(shù)據(jù)全部持久化到磁盤(pán),此后短時(shí)間內(nèi)客戶端落在父region上的請(qǐng)求都會(huì)拋出異常NotServingRegionException。
5. 核心分裂步驟:在.split文件夾下新建兩個(gè)子文件夾,稱之為daughter A、daughter B,并在文件夾中生成reference文件, 分別指向父region中對(duì)應(yīng)文件。這個(gè)步驟是所有步驟中最核心的一個(gè)環(huán)節(jié),生成reference文件日志如下所示: 2017-08-12 11:53:38,158 DEBUG [StoreOpene-0155388346c3c919d3f05d7188e885e0-1] regionserver.StoreFileInfo: reference’hdfs://hdfscluster/hbase-rsgroup/data/default/music/0155388346c3c919d3f05d7188e885e0/cf/d24415c4fb44427b8f698143e5c4d9dc00 其中reference文件名為d24415c4fb44427b8f698143e5c4d9dc.00bb6239169411e4d0ecb6ddfdbacf66,格式看起來(lái)比較特殊,那這種文件名具體什么含義呢?那來(lái)看看該reference文件指向的父region文件,根據(jù)日志可以看到,切分的父region是00bb6239169411e4d0ecb6ddfdbacf66,對(duì)應(yīng)的切分文件是d24415c4fb44427b8f698143e5c4d9dc,可見(jiàn)reference文件名是個(gè)信息量很大的命名方式,如下所示:
除此之外,還需要關(guān)注reference文件的文件內(nèi)容,reference文件是一個(gè)引用文件(并非linux鏈接文件),文件內(nèi)容很顯然不是用戶數(shù)據(jù)。文件內(nèi)容其實(shí)非常簡(jiǎn)單, 主要有兩部分構(gòu)成: 其一是切分點(diǎn)splitkey, 其二是一個(gè)boolean類(lèi)型的變量( true 或者false),true表示該reference文件引用的是父文件的上半部分(top),而false表示引用的是下半部分 (bottom)。為什么存儲(chǔ)的是這兩部分內(nèi)容?且聽(tīng)下文分解。
看官可以使用hadoop命令親自來(lái)查看reference文件的具體內(nèi)容: hadoopdfs-cat/hbase-rsgroup/data/default/music/0155388346c3c919d3f05d7188e885e0/cf/d24415c4fb44427b8f698 6. 父region分裂為兩個(gè)子region后,將daughter A、daughter B拷貝到HBase根目錄下,形成兩個(gè)新的region。
rollback階段: 如果execute階段出現(xiàn)異常,則執(zhí)行rollback操作。為了實(shí)現(xiàn)回滾,整個(gè)切分過(guò)程被分為很多子階段,回滾程序會(huì)根據(jù)當(dāng)前進(jìn)展到哪個(gè)子階段清理對(duì)應(yīng)的垃圾數(shù)據(jù)。代碼中使用 JournalEntryType 來(lái)表征各個(gè)子階段,具體見(jiàn)下圖:
Region切分事務(wù)性保證
整個(gè)region切分是一個(gè)比較復(fù)雜的過(guò)程,涉及到父region中HFile文件的切分、兩個(gè)子region的生成、系統(tǒng)meta元數(shù)據(jù)的更改等很多子步驟,因此必須保證整個(gè)切分過(guò)程的事務(wù)性,即要么切分完全成功,要么切分完全未開(kāi)始,在任何情況下也不能出現(xiàn)切分只完成一半的情況。
為了實(shí)現(xiàn)事務(wù)性,hbase設(shè)計(jì)了使用狀態(tài)機(jī)(見(jiàn)SplitTransaction類(lèi))的方式保存切分過(guò)程中的每個(gè)子步驟狀態(tài),這樣一旦出現(xiàn)異常,系統(tǒng)可以根據(jù)當(dāng)前所處的狀態(tài)決定是否回滾,以及如何回滾。遺憾的是,目前實(shí)現(xiàn)中這些中間狀態(tài)都只存儲(chǔ)在內(nèi)存中,因此一旦在切分過(guò)程中出現(xiàn)regionserver宕機(jī)的情況,有可能會(huì)出現(xiàn)切分處于中間狀態(tài)的情況,也就是RIT狀態(tài)。這種情況下需要使用hbck工具進(jìn)行具體查看并分析解決方案。在2.0版本之后,HBase實(shí)現(xiàn)了新的分布式事務(wù)框架Procedure V2(HBASE-12439),新框架將會(huì)使用HLog存儲(chǔ)這種單機(jī)事務(wù)(DDL操作、Split操作、Move操作等)的中間狀態(tài),因此可以保證即使在事務(wù)執(zhí)行過(guò)程中參與者發(fā)生了宕機(jī),依然可以使用HLog作為協(xié)調(diào)者對(duì)事務(wù)進(jìn)行回滾操作或者重試提交,大大減少甚至杜絕RIT現(xiàn)象。這也是是2.0在可用性方面最值得期待的一個(gè)亮點(diǎn)!!!
Region切分對(duì)其他模塊的影響
通過(guò)region切分流程的了解,我們知道整個(gè)region切分過(guò)程并沒(méi)有涉及數(shù)據(jù)的移動(dòng),所以切分成本本身并不是很高,可以很快完成。切分后子region的文件實(shí)際沒(méi)有任何用戶數(shù)據(jù),文件中存儲(chǔ)的僅是一些元數(shù)據(jù)信息-切分點(diǎn)rowkey等,那通過(guò)引用文件如何查找數(shù)據(jù)呢?子region的數(shù)據(jù)實(shí)際在什么時(shí)候完成真正遷移?數(shù)據(jù)遷移完成之后父region什么時(shí)候會(huì)被刪掉?
這里就會(huì)看到reference文件名、文件內(nèi)容的實(shí)際意義啦。整個(gè)流程如下圖所示:
(1)根據(jù)reference文件名(region名+真實(shí)文件名)定位到真實(shí)數(shù)據(jù)所在文件路徑。
(2)定位到真實(shí)數(shù)據(jù)文件就可以在整個(gè)文件中掃描待查KV了么?非也。因?yàn)閞eference文件通常都只引用了數(shù)據(jù)文件的一半數(shù)據(jù), 以切分點(diǎn)為界,要么上半部分文件數(shù)據(jù),要么下半部分?jǐn)?shù)據(jù)。那到底哪部分?jǐn)?shù)據(jù)?切分點(diǎn)又是哪個(gè)點(diǎn)?還記得上文又提到reference 文件的文件內(nèi)容吧,沒(méi)錯(cuò),就記錄在文件中。
答案是子region發(fā)生major_compaction時(shí),我們知道compaction的執(zhí)行實(shí)際上是將store中所有小文件一個(gè)KV一個(gè)KV從小到大讀出來(lái)之后再順序?qū)懭胍粋€(gè)大文件,完成之后再將小文件刪掉,因此compaction本身就需要讀取并寫(xiě)入大量數(shù)據(jù)。子region執(zhí)行major_compaction后會(huì)將父目錄中屬于該子region的所有數(shù)據(jù)讀出來(lái)并寫(xiě)入子region目錄數(shù)據(jù)文件中。可見(jiàn)將數(shù)據(jù)遷移放到compaction這個(gè)階段來(lái)做,是一件順便的事。
實(shí)際上HMaster會(huì)啟動(dòng)一個(gè)線程定期遍歷檢查所有處于splitting狀態(tài)的父region,確定檢查父region是否可以被清理。檢測(cè)線程首先會(huì)在meta表中揪出所有split列為true的region,并加載出其分裂后生成的兩個(gè)子region(meta表中splitA列和splitB列),只需要檢查此兩個(gè)子region是否還存在引用文件,如果都不存在引用文件就可以認(rèn)為該父region對(duì)應(yīng)的文件可以被刪除。現(xiàn)在再來(lái)看看上文中父目錄在meta表中的信息,就大概可以理解為什么會(huì)存儲(chǔ)這些信息了:
4. split模塊在生產(chǎn)線的一些坑?
有些時(shí)候會(huì)有同學(xué)反饋說(shuō)集群中部分region處于長(zhǎng)時(shí)間RIT,region狀態(tài)為spliting。通常情況下都會(huì)建議使用hbck看下什么報(bào)錯(cuò), 然后再根據(jù)hbck提供的一些工具進(jìn)行修復(fù),hbck提供了部分命令對(duì)處于split狀態(tài)的rit region進(jìn)行修復(fù),主要的命令如下: -fixSplitParents Try to force offline split parents to be online.-removeParents Try to offline and sideline lingering parents and keep daughter regions.-fixReferenceFiles Try to offline lingering reference store files
其中最常見(jiàn)的問(wèn)題是 : ERROR:Foundlingeringreferencefilehdfs://mycluster/hbase/news_user_actions/3b3ae24c65fc5094bc2acfebaa7a56de/ 簡(jiǎn)單解釋一下,這個(gè)錯(cuò)誤是說(shuō)reference文件所引用的父region文件不存在了,如果查看日志的話有可能看到如下異常:java.io.IOException: java.io.IOException: java.io.FileNotFoundException: File does not exist:/hbase/news_user_actions/b7
父region文件為什么會(huì)莫名其妙不存在?經(jīng)過(guò)和朋友的討論,確認(rèn)有可能是因?yàn)楣俜絙ug導(dǎo)致,詳見(jiàn)HBASE-13331。這個(gè)jira是說(shuō)HMaster在確認(rèn)父目錄是否可以被刪除時(shí),如果檢查引用文件(檢查是否存在、檢查是否可以正常打開(kāi))拋出IOException異常, 函數(shù)就會(huì)返回沒(méi)有引用文件,導(dǎo)致父region被刪掉。正常情況下應(yīng)該保險(xiǎn)起見(jiàn)返回存在引用文件,保留region,并打印日志手工介入查看。如果大家也遇到類(lèi)似的問(wèn)題,可以看看這個(gè)問(wèn)題,也可以將修復(fù)patch打到線上版本或者升級(jí)版本。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的HBase读写流程、flush、文件合并、region拆分的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: i99900k设计配什么主板
- 下一篇: git fock的子项目从上游仓库(源项