這篇文章主要講解了“HBase WAL源碼分析”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“HBase WAL源碼分析”吧!
網(wǎng)站設(shè)計(jì)制作過程拒絕使用模板建站;使用PHP+MYSQL原生開發(fā)可交付網(wǎng)站源代碼;符合網(wǎng)站優(yōu)化排名的后臺管理系統(tǒng);網(wǎng)站制作、做網(wǎng)站收費(fèi)合理;免費(fèi)進(jìn)行網(wǎng)站備案等企業(yè)網(wǎng)站建設(shè)一條龍服務(wù).我們是一家持續(xù)穩(wěn)定運(yùn)營了10多年的創(chuàng)新互聯(lián)建站網(wǎng)站建設(shè)公司。
WAL(Write-Ahead Logging)是數(shù)據(jù)庫系統(tǒng)中保障原子性和持久性的技術(shù),通過使用WAL可以將數(shù)據(jù)的隨機(jī)寫入變?yōu)轫樞驅(qū)懭?,可以提高?shù)據(jù)寫入的性能。在hbase中寫入數(shù)據(jù)時,會將數(shù)據(jù)寫入內(nèi)存同時寫wal日志,為防止日志丟失,日志是寫在hdfs上的。
默認(rèn)是每個RegionServer有1個WAL,在HBase1.0開始支持多個WALHBASE-5699,這樣可以提高寫入的吞吐量。配置參數(shù)為hbase.wal.provider=multiwal,支持的值還有defaultProvider和filesystem(這2個是同樣的實(shí)現(xiàn))。
WAL的持久化的級別有如下幾種:
SKIP_WAL:不寫wal日志,這種可以較大提高寫入的性能,但是會存在數(shù)據(jù)丟失的危險(xiǎn),只有在大批量寫入的時候才使用(出錯了可以重新運(yùn)行),其他情況不建議使用。
ASYNC_WAL:異步寫入
SYNC_WAL:同步寫入wal日志文件,保證數(shù)據(jù)寫入了DataNode節(jié)點(diǎn)。
FSYNC_WAL: 目前不支持了,表現(xiàn)是與SYNC_WAL是一致的
USE_DEFAULT: 如果沒有指定持久化級別,則默認(rèn)為USE_DEFAULT, 這個為使用HBase全局默認(rèn)級別(SYNC_WAL)
先看看wal寫入中的幾個主要的類
1. WALKey:wal日志的key,包括regionName:日志所屬的region
tablename:日志所屬的表,writeTime:日志寫入時間,clusterIds:cluster的id,在數(shù)據(jù)復(fù)制的時候會用到。
2.WALEdit:在hbase的事務(wù)日志中記錄一系列的修改的一條事務(wù)日志。另外WALEdit實(shí)現(xiàn)了Writable接口,可用于序列化處理。
3. FSHLog: WAL的實(shí)現(xiàn)類,負(fù)責(zé)將數(shù)據(jù)寫入文件系統(tǒng)
在每個wal的寫入這里使用的是多生產(chǎn)者單消費(fèi)者的模式,這里使用到了disruptor框架,將WALKey和WALEdit信息封裝為FSWALEntry,然后通過RingBufferTruck放入RingBuffer中。接下來看hlog的寫入流程,分為以下3步:
日志寫入緩存:由rpcHandler將日志信息寫入緩存ringBuffer.
緩存數(shù)據(jù)寫入文件系統(tǒng):每個FSHLog有一個線程負(fù)責(zé)將數(shù)據(jù)寫入文件系統(tǒng)(HDFS)
數(shù)據(jù)同步:如果操作的持久化級別為(SYNC_WAL或者USE_DEFAULT 則需進(jìn)行數(shù)據(jù)同步處理
下面來詳細(xì)說明一下各類線程是如何配合來實(shí)現(xiàn)這幾步操作的,
rpcHandler線程負(fù)責(zé)將日志信息(FSWALEntry)寫入緩存RingBbuffer,在操作日志寫完后,rpcHandler會調(diào)用wal的sync方法,進(jìn)行數(shù)據(jù)同步,其實(shí)際處理為寫入一個SyncFuture到RingBuffer,然后blocking一直到syncFuture處理完成。
wal線程從緩存RingBuffer中取數(shù)據(jù),如果為日志(FSWALEntry)就調(diào)用Writer將數(shù)據(jù)寫入文件系統(tǒng),如果為SyncFuture,則由專門的同步線程來進(jìn)行同步處理。
整體處理流程圖如下:
wal寫入文件系統(tǒng)是通過Writer來寫入的,其實(shí)際類為ProtobufLogWriter,使用的是Protobuf的格式持久化處理。使用Protobuf格式有如下優(yōu)勢:
性能較高
結(jié)構(gòu)更加緊湊,節(jié)省空間
方便擴(kuò)展以及支持其他語言,通過其他語言來解析日志。
寫入的日志中是按WALKey和WALEdit來依次存儲的(具體內(nèi)容見前面WALKey和WALEdit類的說明),另外還將WALKey和WALEdit分別進(jìn)行了壓縮處理。
每個wal中有一個RingBufferEventHandler對象,其中用數(shù)組管理著多個SyncRunner線程(由參數(shù)hbase.regionserver.hlog.syncer.count配置,默認(rèn)5)來進(jìn)行同步處理,每個SyncRunner對象里面有一個LinkedBlockingQueue(syncFutures,大小為參數(shù){hbase.regionserver.handler.count默認(rèn)值200}*3
另外這里的SyncFuture是每個rpcHandler線程擁有一個,由wal中的private final Map
class RingBufferEventHandler implements EventHandler, LifecycleAware { private final SyncRunner [] syncRunners; private final SyncFuture [] syncFutures; ... } private class SyncRunner extends HasThread { private volatile long sequence; // Keep around last exception thrown. Clear on successful sync. private final BlockingQueue syncFutures; ... }
這里在處理ringBuffer中的syncFuture時,不是每有一個就提交到syncRunner處理,而是按批來處理的,這里的批分2種情況:
從ringBuffer中取到的一批數(shù)據(jù)(為提高效率,在disruptor框架中是按批從ringBuffer中取數(shù)據(jù)的,具體的請看disruptor的相關(guān)文檔),如果這批數(shù)據(jù)中的syncFuture個數(shù)<{hbase.regionserver.handler.count默認(rèn)值200},則按一批處理
如果這一批數(shù)據(jù)中的syncFuture個數(shù)>={hbase.regionserver.handler.count默認(rèn)值200}個數(shù),則按{hbase.regionserver.handler.count默認(rèn)值200}分批處理。
如果達(dá)到了批大小,就從syncRunner數(shù)組中順序選擇下一個SyncRunner,將這批數(shù)據(jù)插入該SyncRunner的BlockingQueue中。最后由SyncRunner線程進(jìn)行hdfs文件同步處理。為保證數(shù)據(jù)的不丟失,rpc請求需要保證wal日志寫入成功后才能返回,這里HBase做了一系列的優(yōu)化處理的操作。
通過wal日志切換,這樣可以避免產(chǎn)生單獨(dú)的過大的wal日志文件,這樣可以方便后續(xù)的日志清理(可以將過期日志文件直接刪除)另外如果需要使用日志進(jìn)行恢復(fù)時,也可以同時解析多個小的日志文件,縮短恢復(fù)所需時間。
wal觸發(fā)切換的場景有如下幾種:
SyncRunner線程在處理日志同步后,如果有異常發(fā)生,就會調(diào)用requestLogRoll發(fā)起日志滾動請求
SyncRunner線程在處理日志同步后, 檢查當(dāng)前在寫的wal的日志大小是否超過配置{hbase.regionserver.hlog.blocksize默認(rèn)為hdfs目錄塊大小}*{hbase.regionserver.logroll.multiplier默認(rèn)0.95},超過后同樣調(diào)用requestLogRoll發(fā)起日志滾動請求
每個RegionServer有一個LogRoller線程會定期滾動日志,滾動周期由參數(shù){hbase.regionserver.logroll.period默認(rèn)值1個小時}控制
這里前面2種場景調(diào)用requestLogRoll發(fā)起日志滾動請求,最終也是通過LogRoller來執(zhí)行日志滾動的操作。
當(dāng)memstore中的數(shù)據(jù)刷新到hdfs后,那對應(yīng)的wal日志就不需要了,F(xiàn)SHLog中有記錄當(dāng)前memstore中各region對應(yīng)的最老的sequenceId,如果一個日志中的各個region的操作的最新的sequenceId均小于wal中記錄的各個需刷新的region的最老sequenceId,說明該日志文件就不需要了,于是就會將該日志文件從./WALs目錄移動到./oldWALs目錄。這塊是在前面日志滾動完成后調(diào)用cleanOldLogs來處理的。
由于wal日志還會用于跨集群的同步處理,所以wal日志失效后并不會立即刪除,而是移動到oldWALs目錄。由HMaster中的LogCleaner這個Chore線程來負(fù)責(zé)wal日志的刪除,在LogCleaner內(nèi)部通過參數(shù){hbase.master.logcleaner.plugins}以插件的方式來篩選出可以刪除的日志文件。目前配置的插件有ReplicationLogCleaner、SnapshotLogCleaner和TimeToLiveLogCleaner
TimeToLiveLogCleaner: 日志文件最后修改時間在配置參數(shù){hbase.master.logcleaner.ttl默認(rèn)600秒}之前的可以刪除
ReplicationLogCleaner:如果有跨集群數(shù)據(jù)同步的需求,通過該Cleaner來保證那些在同步中的日志不被刪除
SnapshotLogCleaner: 被表的snapshot使用到了的wal不被刪除
感謝各位的閱讀,以上就是“HBase WAL源碼分析”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對HBase WAL源碼分析這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!