日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

HBase读写流程、flush、文件合并、region拆分

發(fā)布時(shí)間:2023/12/19 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HBase读写流程、flush、文件合并、region拆分 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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):

  • regionserver 更改ZK節(jié)點(diǎn) /region-in-transition 中該region的狀態(tài)為SPLITING。
  • master通過(guò)watch節(jié)點(diǎn)/region-in-transition檢測(cè)到region狀態(tài)改變,并修改內(nèi)存中region的狀態(tài),在master頁(yè)面RIT模塊就可以看到region執(zhí)行split的狀態(tài)信息。
  • 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。

  • parent region通知修改 hbase.meta 表后下線,不再提供服務(wù)。下線后parent region在meta表中的信息并不會(huì)馬上刪除, 而是標(biāo)注split列、offline列為true,并記錄兩個(gè)子region。為什么不立馬刪除?且聽(tīng)下文分解。
  • 開(kāi)啟daughter A、daughter B兩個(gè)子region。通知修改 hbase.meta 表,正式對(duì)外提供服務(wù)。

    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ì)被刪掉?

  • 通過(guò)reference文件如何查找數(shù)據(jù)?
  • 這里就會(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的數(shù)據(jù)什么時(shí)候會(huì)遷移到子region目錄?
  • 答案是子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)做,是一件順便的事。

  • 父region什么時(shí)候會(huì)被刪除?
  • 實(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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。