HBase - 数据写入流程解析
本文由??網(wǎng)易云?發(fā)布。
作者:范欣欣
本篇文章僅限內(nèi)部分享,如需轉(zhuǎn)載,請(qǐng)聯(lián)系網(wǎng)易獲取授權(quán)。
?
?
眾所周知,HBase默認(rèn)適用于寫(xiě)多讀少的應(yīng)用,正是依賴于它相當(dāng)出色的寫(xiě)入性能:一個(gè)100臺(tái)RS的集群可以輕松地支撐每天10T?的寫(xiě)入量。當(dāng)然,為了支持更高吞吐量的寫(xiě)入,HBase還在不斷地進(jìn)行優(yōu)化和修正,這篇文章結(jié)合0.98版本的源碼全面地分析HBase的寫(xiě)入流程,全文分為三個(gè)部分,第一部分介紹客戶端的寫(xiě)入流程,第二部分介紹服務(wù)器端的寫(xiě)入流程,最后再重點(diǎn)分析WAL的工作原理(注:從服務(wù)器端的角度理解,HBase寫(xiě)入分為兩個(gè)階段,第一階段數(shù)據(jù)會(huì)被寫(xiě)入memstore,并且會(huì)執(zhí)行WAL的寫(xiě)入;第二階段會(huì)將memstore的中的數(shù)據(jù)集中flush到磁盤(pán),本文主要集中分析第一階段的相關(guān)細(xì)節(jié))。
?
客戶端流程解析
(1)用戶提交put請(qǐng)求后,HBase客戶端會(huì)將put請(qǐng)求添加到本地buffer中,符合一定條件就會(huì)通過(guò)AsyncProcess異步批量提交。HBase默認(rèn)設(shè)置autoflush=true,表示put請(qǐng)求直接會(huì)提交給服務(wù)器進(jìn)行處理;用戶可以設(shè)置autoflush=false,這樣的話put請(qǐng)求會(huì)首先放到本地buffer,等到本地buffer大小超過(guò)一定閾值(默認(rèn)為2M,可以通過(guò)配置文件配置)之后才會(huì)提交。很顯然,后者采用group commit機(jī)制提交請(qǐng)求,可以極大地提升寫(xiě)入性能,但是因?yàn)闆](méi)有保護(hù)機(jī)制,如果客戶端崩潰的話會(huì)導(dǎo)致提交的請(qǐng)求丟失。
(2)在提交之前,HBase會(huì)在元數(shù)據(jù)表.meta.中根據(jù)rowkey找到它們歸屬的region server,這個(gè)定位的過(guò)程是通過(guò)HConnection 的locateRegion方法獲得的。如果是批量請(qǐng)求的話還會(huì)把這些rowkey按照HRegionLocation分組,每個(gè)分組可以對(duì)應(yīng)一次RPC請(qǐng)求。
(?3 )HBase 會(huì)為每個(gè)HRegionLocation 構(gòu)造一個(gè)遠(yuǎn)程RPC 請(qǐng)求MultiServerCallable<Row> , 然后通過(guò)rpcCallerFactory.<MultiResponse> newCaller()執(zhí)行調(diào)用,忽略掉失敗重新提交和錯(cuò)誤處理,客戶端的提交操作到此結(jié)束。服務(wù)器端流程解析服務(wù)器端RegionServer接收到客戶端的寫(xiě)入請(qǐng)求后,首先會(huì)反序列化為Put對(duì)象,然后執(zhí)行各種檢查操作,比如檢查region是否是只讀、memstore大小是否超過(guò)blockingMemstoreSize等。檢查完成之后,就會(huì)執(zhí)行如下核心操作:
(1)?獲取行鎖、Region更新共享鎖: HBase中使用行鎖保證對(duì)同一行數(shù)據(jù)的更新都是互斥操作,用以保證更新的原子性,要么更新成功,要么失敗。
(2)?開(kāi)始寫(xiě)事務(wù):獲取write number,用于實(shí)現(xiàn)MVCC,實(shí)現(xiàn)數(shù)據(jù)的非鎖定讀,在保證讀寫(xiě)一致性的前提下提高讀取性能。
(3)?寫(xiě)緩存memstore:HBase中每個(gè)列族都會(huì)對(duì)應(yīng)一個(gè)store,用來(lái)存儲(chǔ)該列族數(shù)據(jù)。每個(gè)store都會(huì)有個(gè)寫(xiě)緩存memstore,用于緩存寫(xiě)入數(shù)據(jù)。HBase并不會(huì)直接將數(shù)據(jù)落盤(pán),而是先寫(xiě)入緩存,等緩存滿足一定大小之后再一起落盤(pán)。
(4)?Append?HLog:HBase使用WAL機(jī)制保證數(shù)據(jù)可靠性,即首先寫(xiě)日志再寫(xiě)緩存,即使發(fā)生宕機(jī),也可以通過(guò)恢復(fù)HLog還原出原始數(shù)據(jù)。該步驟就是將數(shù)據(jù)構(gòu)造為WALEdit對(duì)象,然后順序?qū)懭際Log中,此時(shí)不需要執(zhí)行sync操作。0.98版本采用了新的寫(xiě)線程模式實(shí)現(xiàn)HLog日志的寫(xiě)入,可以使得整個(gè)數(shù)據(jù)更新性能得到極大提升,具體原理見(jiàn)下一個(gè)章節(jié)。
(5)釋放行鎖以及共享鎖
(6)Sync HLog真正sync到HDFS,在釋放行鎖之后執(zhí)行sync操作是為了盡量減少持鎖時(shí)間,提升寫(xiě)性能。如果Sync失敗,執(zhí)行回滾操作將memstore中已經(jīng)寫(xiě)入的數(shù)據(jù)移除。
?(7)?結(jié)束寫(xiě)事務(wù):此時(shí)該線程的更新操作才會(huì)對(duì)其他讀請(qǐng)求可見(jiàn),更新才實(shí)際生效。具體分析見(jiàn)上一篇文章《HBase?- 并發(fā)控制深度解析》
(8)?flush?memstore:當(dāng)寫(xiě)緩存滿64M之后,會(huì)啟動(dòng)flush線程將數(shù)據(jù)刷新到硬盤(pán)。刷新操作涉及到HFile相關(guān)結(jié)構(gòu),后面會(huì)詳細(xì)對(duì)此進(jìn)行介紹。
?
WAL機(jī)制解析
?WAL(Write-Ahead Logging)是一種高效的日志算法,幾乎是所有非內(nèi)存數(shù)據(jù)庫(kù)提升寫(xiě)性能的不二法門(mén),基本原理是在數(shù)據(jù)寫(xiě)入之前首先順序?qū)懭肴罩?#xff0c;然后再寫(xiě)入緩存,等到緩存寫(xiě)滿之后統(tǒng)一落盤(pán)。之所以能夠提升寫(xiě)性能,是因?yàn)閃AL將一次隨機(jī)寫(xiě)轉(zhuǎn)化為了一次順序?qū)懠右淮蝺?nèi)存寫(xiě)。提升寫(xiě)性能的同時(shí),WAL可以保證數(shù)據(jù)的可靠性,即在任何情況下數(shù)據(jù)不丟失。假如一次寫(xiě)入完成之后發(fā)生了宕機(jī),即使所有緩存中的數(shù)據(jù)丟失,也可以通過(guò)恢復(fù)日志還原出丟失的數(shù)據(jù)。
?
WAL持久化等級(jí) ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
HBase中可以通過(guò)設(shè)置WAL的持久化等級(jí)決定是否開(kāi)啟WAL機(jī)制、以及HLog的落盤(pán)方式。WAL的持久化等級(jí)分為如下四個(gè)等級(jí):
1.?SKIP_WAL:只寫(xiě)緩存,不寫(xiě)HLog日志。這種方式因?yàn)橹粚?xiě)內(nèi)存,因此可以極大的提升寫(xiě)入性能,但是數(shù)據(jù)有丟失的風(fēng)險(xiǎn)。在實(shí)際應(yīng)用過(guò)程中并不建議設(shè)置此等級(jí),除非確認(rèn)不要求數(shù)據(jù)的可靠性。
2.?ASYNC_WAL:異步將數(shù)據(jù)寫(xiě)入HLog日志中。
?3.?SYNC_WAL:同步將數(shù)據(jù)寫(xiě)入日志文件中,需要注意的是數(shù)據(jù)只是被寫(xiě)入文件系統(tǒng)中,并沒(méi)有真正落盤(pán)。
?4.?FSYNC_WAL:同步將數(shù)據(jù)寫(xiě)入日志文件并強(qiáng)制落盤(pán)。最嚴(yán)格的日志寫(xiě)入等級(jí),可以保證數(shù)據(jù)不會(huì)丟失,但是性能相對(duì)比較差。
?5.?USER_DEFAULT:默認(rèn)如果用戶沒(méi)有指定持久化等級(jí),HBase使用SYNC_WAL等級(jí)持久化數(shù)據(jù)。
用戶可以通過(guò)客戶端設(shè)置WAL持久化等級(jí),代碼:put.setDurability(Durability. SYNC_WAL );?
?
HLog數(shù)據(jù)結(jié)構(gòu)
HBase中,WAL的實(shí)現(xiàn)類(lèi)為HLog,每個(gè)Region Server擁有一個(gè)HLog日志,所有region的寫(xiě)入都是寫(xiě)到同一個(gè)HLog。下圖表示同一個(gè)Region Server中的3個(gè) region 共享一個(gè)HLog。當(dāng)數(shù)據(jù)寫(xiě)入時(shí),是將數(shù)據(jù)對(duì)<HLogKey,WALEdit>按照順序追加到HLog 中,以獲取最好的寫(xiě)入性能。
?
上圖中HLogKey主要存儲(chǔ)了log sequence number,更新時(shí)間 write time,region name,表名table name以及cluster ids。其中l(wèi)og sequncece number作為HFile中一個(gè)重要的元數(shù)據(jù),和HLog的生命周期息息相關(guān),后續(xù)章節(jié)會(huì)詳細(xì)介紹;region name和table name分別表征該段日志屬于哪個(gè)region以及哪張表;cluster ids用于將日志復(fù)制到集群中其他機(jī)器上。
?
WALEdit用來(lái)表示一個(gè)事務(wù)中的更新集合,在之前的版本,如果一個(gè)事務(wù)中對(duì)一行row R中三列c1,c2,c3分別做了修改,那么hlog中會(huì)有3個(gè)對(duì)應(yīng)的日志片段如下所示:
<logseq3-for-edit3>:<keyvalue-for-edit-c3>
?
然而,這種日志結(jié)構(gòu)無(wú)法保證行級(jí)事務(wù)的原子性,假如剛好更新到c2之后發(fā)生宕機(jī),那么就會(huì)產(chǎn)生只有部分日志寫(xiě)入成功的現(xiàn)象。為此,hbase將所有對(duì)同一行的更新操作都表示為一個(gè)記錄,如下:
?<logseq#-for-entire-txn>:<WALEdit-for-entire-txn>
?
其中WALEdit會(huì)被序列化為格式<-1, # of edits, <KeyValue>, <KeyValue>, <KeyValue>>,比如<-1, 3, <keyvalue-for-edit- c1>, <keyvalue-for-edit-c2>, <keyvalue-for-edit-c3>>,其中-1作為標(biāo)示符表征這種新的日志結(jié)構(gòu)。
?
WAL寫(xiě)入模型
了解了HLog 的結(jié)構(gòu)之后, 我們就開(kāi)始研究HLog 的寫(xiě)入模型。 HLog 的寫(xiě)入可以分為三個(gè)階段, 首先將數(shù)據(jù)對(duì)<HLogKey,WALEdit>寫(xiě)入本地緩存,然后再將本地緩存寫(xiě)入文件系統(tǒng),最后執(zhí)行sync操作同步到磁盤(pán)。在以前老的寫(xiě)入模型中, 上述三步都由工作線程獨(dú)自完成,如下圖所示:
上圖中,本地緩存寫(xiě)入文件系統(tǒng)那個(gè)步驟工作線程需要持有updateLock執(zhí)行,不同工作線程之間必然會(huì)惡性競(jìng)爭(zhēng);不僅如此,在Sync HDFS這步中,工作線程之間需要搶占flushLock,因?yàn)镾ync操作是一個(gè)耗時(shí)操作,搶占這個(gè)鎖會(huì)導(dǎo)致寫(xiě)入性能大幅降低。
所幸的是,來(lái)自中國(guó)(準(zhǔn)確的來(lái)說(shuō),是來(lái)自小米,鼓掌)的3位工程師意識(shí)到了這個(gè)問(wèn)題,進(jìn)而提出了一種新的寫(xiě)入模型并被官方 采納。根據(jù)官方測(cè)試,新寫(xiě)入模型的吞吐量比之前提升3倍多,單臺(tái)RS寫(xiě)入吞吐量介于12150~31520,5臺(tái)RS組成的集群寫(xiě)入吞吐量介于22000~70000(見(jiàn)HBASE-8755)。下圖是小米官方給出來(lái)的對(duì)比測(cè)試結(jié)果:
?
在新寫(xiě)入模型中,本地緩存寫(xiě)入文件系統(tǒng)以及Sync HDFS都交給了新的獨(dú)立線程完成,并引入一個(gè)Notify線程通知工作線程是否已經(jīng)Sync成功,采用這種機(jī)制消除上述鎖競(jìng)爭(zhēng),具體如下圖所示:
?
?
1.?上文中提到工作線程在寫(xiě)入WALEdit?之后并沒(méi)有進(jìn)行Sync?,?而是等到釋放行鎖阻塞在syncedTillHere?變量上,?等待AsyncNotifier線程喚醒。
2.?工作線程將WALEdit寫(xiě)入本地Buffer之后,會(huì)生成一個(gè)自增變量txid,攜帶此txid喚醒AsyncWriter線程
3.?AsyncWriter?線程會(huì)取出本地Buffer?中的所有WALEdit?,?寫(xiě)入HDFS?。注意該線程會(huì)比較傳入的txid?和已經(jīng)寫(xiě)入的最大txid(writtenTxid),如果傳入的txid小于writteTxid,表示該txid對(duì)應(yīng)的WALEdit已經(jīng)寫(xiě)入,直接跳過(guò)
4.?AsyncWriter線程將所有WALEdit寫(xiě)入HDFS之后攜帶maxTxid喚醒AsyncFlusher線程?
5.?AsyncFlusher線程將所有寫(xiě)入文件系統(tǒng)的WALEdit統(tǒng)一Sync刷新到磁盤(pán)
6.?數(shù)據(jù)全部落盤(pán)之后調(diào)用setFlushedTxid方法喚醒AyncNotifier線程
7.?AyncNotifier線程會(huì)喚醒所有阻塞在變量syncedTillHere的工作線程,工作線程被喚醒之后表示W(wǎng)AL寫(xiě)入完成,后面再執(zhí)行MVCC結(jié)束寫(xiě)事務(wù),推進(jìn)全局讀取點(diǎn),本次更新才會(huì)對(duì)用戶可見(jiàn)
?
通過(guò)上述過(guò)程的梳理可以知道,新寫(xiě)入模型采取了多線程模式獨(dú)立完成寫(xiě)文件系統(tǒng)、sync磁盤(pán)操作,避免了之前多工作線程惡性搶占鎖的問(wèn)題。同時(shí),工作線程在將WALEdit寫(xiě)入本地Buffer之后并沒(méi)有馬上阻塞,而是釋放行鎖之后阻塞等待WALEdit落盤(pán),這樣可以盡可能地避免行鎖競(jìng)爭(zhēng),提高寫(xiě)入性能。
?
總結(jié)
文章剛開(kāi)始就提到HBase寫(xiě)入分為兩個(gè)階段,本文主要集中分析第一階段的相關(guān)細(xì)節(jié),首先介紹了HBase的寫(xiě)入memstore的流程,之后重點(diǎn)分析了WAL的寫(xiě)入模型以及相關(guān)優(yōu)化。后面一篇文章會(huì)接著介紹寫(xiě)入到memstore的數(shù)據(jù)落盤(pán)的相關(guān)知識(shí)點(diǎn),敬請(qǐng)期待!
?
網(wǎng)易有數(shù):企業(yè)級(jí)大數(shù)據(jù)可視化分析平臺(tái)。面向業(yè)務(wù)人員的自助式敏捷分析平臺(tái),采用PPT模式的報(bào)告制作,更加易學(xué)易用,具備強(qiáng)大的探索分析功能,真正幫助用戶洞察數(shù)據(jù)發(fā)現(xiàn)價(jià)值。可點(diǎn)擊這里免費(fèi)試用。
?
了解 網(wǎng)易云 :
網(wǎng)易云官網(wǎng):https://www.163yun.com/
新用戶大禮包:https://www.163yun.com/gift
網(wǎng)易云社區(qū):https://sq.163yun.com/
轉(zhuǎn)載于:https://www.cnblogs.com/163yun/p/9020608.html
總結(jié)
以上是生活随笔為你收集整理的HBase - 数据写入流程解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Eclipse如何从SVN更新和上传修改
- 下一篇: python-包package