[翻譯]HBase 架構(gòu)101 –預(yù)寫日志系統(tǒng) (WAL)HBase 架構(gòu)101 –預(yù)寫日志系統(tǒng) (WAL) 原文:http://www./2010/01/hbase-architecture-101-write-ahead-log.html 什么是預(yù)寫日志WAL? 之前的文章我們簡單介紹了HBase的存儲結(jié)構(gòu)。其中提到了預(yù)寫日志。這里,我們要介紹它的實現(xiàn)細(xì)節(jié),所有的描述都基于HBase 0.20.3.
WAL最重要的作用是災(zāi)難恢復(fù)。和MySQL 的BIN log類似,它記錄所有的數(shù)據(jù)改動。一旦服務(wù)器崩潰,通過重放log,我們可以恢復(fù)崩潰之前的數(shù)據(jù)。這也意味如果寫入WAL失敗,整個操作將認(rèn)為失敗。 我們先看看HBase是如何做到的。首先,客戶端初始化一個可能對數(shù)據(jù)改動的操作,如put(Put),delete(Delete) 和 incrementColumnValue()。這些操作都將被封裝在一個KeyValue對象實例中,通過RPC 調(diào)用發(fā)送給HRegionServer(最好是批量操作)。 一旦達到一定大小,HRegionServer 將其發(fā)送給HRegion。這個過程中,數(shù)據(jù)會首先會被寫入WAL,之后將被寫到實際存放數(shù)據(jù)的MemStore中。 當(dāng)MemStore到達一定大小,或者經(jīng)過一段時間后,數(shù)據(jù)將被異步地寫入文件系統(tǒng)中。然而,在兩次寫入文件系統(tǒng)之間的數(shù)據(jù),是保留在內(nèi)存中的。如果這個時候系統(tǒng)崩潰,那數(shù)據(jù)···,別急,我們有WAL! 我們先來看看WAL幾個重要的類。 HLogHLog是實現(xiàn)WAL的類。一個HRegionServer對應(yīng)一個HLog實例。當(dāng)HRegion初始化時,HLog將作為一個參數(shù)傳給HRegion的構(gòu)造函數(shù)。 HLog最核心的是調(diào)用doWrite的append() 方法,前面提到的可能對數(shù)據(jù)改動的操作都就將首先調(diào)用這個方法。出于性能的考慮,put(), delete() 和incrementColumnValue()有一個開關(guān)函數(shù)setWriteToWAL(boolean) , 設(shè)為false將禁用WAL。這是為什么上圖中向下的箭頭是虛線的原因。默認(rèn)時候當(dāng)然需要WAL,但是假如你運行一個數(shù)據(jù)導(dǎo)入的MapReduce Job,你可以通過關(guān)閉WAL獲得性能上的提升。 另一個重要的特性是HLog將通過“sequence number”追蹤數(shù)據(jù)改變。它內(nèi)部使用AtomicLong保證線程安全。sequence number的起始值為0,或者是最近一次存入文件系統(tǒng)中sequence number。Region打開存儲文件,讀取每個HFile中的最大的sequence number,如果該值大于HLog 的sequence number, 就將它作為HLog 的sequence number的值。最后,HLog將得到上次存入文件和繼續(xù)記log的點。過會,我們將看到它的應(yīng)用。
上圖表示了3個不同的region,每一個負(fù)責(zé)一段rowkey的范圍。這些region將共享同一個HLog實例,我們可以看出,從不同region來的數(shù)據(jù)寫入WAL的順序是不確定的。在后面我們會再詳細(xì)的介紹。 最后,Hlog利用HMaster恢復(fù)和切分一個由一個崩潰的HRegionServery遺留下來的Log。之后,重新部署regions。 HLogKeyWAL使用Hadoop的SequenceFile,它將記錄存儲為key/values 的數(shù)據(jù)集。對于WAL,key是一個HLogKey的實例。如前一篇文章中提到的, KeyValue不僅包括row,column family, qualifier, timestamp, value, 還包括“Key Type”—派上用場啦, 這里,可以用Key Type代表一個“put”或“delete”操作。 但是,哪里去存放KeyValue的歸屬信息,比如region或者表名呢?這些存放在HLogKey中。同時還包括 sequence number,和“寫入時間”, 是一個記錄數(shù)據(jù)何時寫入到log的時間戳。 LogFlusher前面提到,數(shù)據(jù)以KeyValue形式到達HRegionServer,將寫入WAL,之后,寫入一個SequenceFile??催^去沒問題,但是因為數(shù)據(jù)流在寫入文件系統(tǒng)時,經(jīng)常會緩存以提高性能。這樣,有些本以為在日志文件中的數(shù)據(jù)實際在內(nèi)存中。這里,我們提供了一個LogFlusher的類。它調(diào)用HLog.optionalSync(),后者根據(jù)“hbase.regionserver.optionallogflushinterval”(默認(rèn)是10秒),定期調(diào)用Hlog.sync()。另外,HLog.doWrite()也會根據(jù)“hbase.regionserver.flushlogentries”(默認(rèn)100秒)定期調(diào)用Hlog.sync()。Sync() 本身調(diào)用HLog.Writer.sync(),它由SequenceFileLogWriter實現(xiàn)。 LogRollerLog的大小通過$HBASE_HOME/conf/hbase-site.xml 的“hbase.regionserver.logroll.period”限制,默認(rèn)是一個小時。所以每60分鐘,會打開一個新的log文件。久而久之,會有一大堆的文件需要維護。首先,LogRoller調(diào)用HLog.rollWriter(),定時滾動日志,之后,利用HLog.cleanOldLogs()可以清除舊的日志。它首先取得存儲文件中的最大的sequence number,之后檢查是否存在一個log所有的條目的“sequence number”均低于這個值,如果存在,將刪除這個log。 這里解釋下你可能在log中看到的令人費解的內(nèi)容:
這里,我們看到,log file的數(shù)目超過了log files的最大值。這時,會強制調(diào)用flush out 以減少log的數(shù)目。 “hbase.regionserver.hlog.blocksize”和“hbase.regionserver.logroll.multiplier”兩個參數(shù)默認(rèn)將在log大小為SequenceFile(默認(rèn)為64MB)的95%時回滾。所以,log的大小和log使用的時間都會導(dǎo)致回滾,以先到達哪個限定為準(zhǔn)。 Replay當(dāng)HRegionServer啟動,打開所管轄的region,它將檢查是否存在剩余的log文件,如果存在,將調(diào)用Store.doReconstructionLog()。重放一個日志只是簡單地讀入一個日志,將日志中的條目加入到Memstore中。最后,flush操作將Memstore中數(shù)據(jù)flush到硬盤中。 舊日志往往由region server 崩潰所產(chǎn)生。當(dāng)HMaster啟動或者檢測到region server 崩潰,它將日志文件拆分為多份文件,將其存儲在region所屬的文件夾。之后,根據(jù)上面提到的方法,將日志重放。需要指出的是,崩潰的服務(wù)器中的region只有在日志被拆分和拷貝之后才能被重新分配。拆分日志利用HLog.splitLog()。舊日志被讀入主線程內(nèi)存中,之后,利用線程池將其寫入所有的region文件夾中,一個線程對應(yīng)于一個region。 問題1. 為什么要一個RegionServer 對應(yīng)于一個HLog。為什么不是一個region對應(yīng)于一個log file? 引用BigTable中的一段話, 如果我們每一個“tablet”(對應(yīng)于HBase的region)都提交一個日志文件,會需要并發(fā)寫入大量的文件到GFS,這樣,根據(jù)每個GFS server所依賴的文件系統(tǒng),寫入不同的日志文件會造成大量的磁盤操作。 HBase依照這樣的原則。在日志被回滾和安全刪除之前,將會有大量的文件。如果改成一個region對應(yīng)于一個文件,將會不好擴展,遲早會引發(fā)問題。 2. 潛在問題 1) 當(dāng)server崩潰,HBase需要將其log切分成合適的片。然而,由于所有的條目混雜在日志中,HMaster只有在將log完全分配到每一個server后,才能將崩潰server中的region重新分配。這個時間可能很長。 2) 數(shù)據(jù)安全。你希望能保存你所有的數(shù)據(jù),雖然你能將flush的時間調(diào)到盡可能的低,你依然依賴于上面提到的文件系統(tǒng)。那些用于存儲數(shù)據(jù)依舊有可能沒寫到磁盤而發(fā)生數(shù)據(jù)丟失。 很明顯,需要log來保證數(shù)據(jù)安全。最好是能讓一個日志保持1個小時(或長)的打開狀態(tài)。當(dāng)數(shù)據(jù)來時,將新的key/value對寫入SequenceFile中,并定期flush數(shù)據(jù)到磁盤中。但是Hadoop不是這樣工作的。他提供了一個API,允許打開一個文件,寫入大量的數(shù)據(jù),然后馬上關(guān)閉文件,成為一個對其他人只讀的文件。只有當(dāng)文件關(guān)閉時才是對其他人可讀的。那么,如果一個進程在寫入文件時僵死,那么,數(shù)據(jù)很可能會丟失。因此,我們需要一個功能,能取到一個離崩潰服務(wù)器寫入數(shù)據(jù)盡可能近的點。 插曲: HDFS append,hflush,hsync,sync··· 這些都起源于HADOOP-1700。Hadoop 0.19沒有能解決這個問題。這個問題后來又成為HADOOP-4379或HDFS-200,并實現(xiàn)了syncFS(),后者可以同步文件的改變。同時,HBase-1470中,將這個API開放,但是依然沒有解決這個問題。 之后是HDFS-265,重新提出append的方案,并引入Syncable的接口,開放hsync()和hflush()。 SequenceFile.Writer.sync()和上面的并不相同。 它只是將一個同步標(biāo)記寫入文件,以方便數(shù)據(jù)恢復(fù)。 雖然append對于HDFS很有用,但并沒有用在HBase中。HBase用了hflush,它可以在log寫完成后將所有數(shù)據(jù)寫入磁盤。當(dāng)服務(wù)器崩潰,我們可以安全地將“臟”文件讀到最后一次改動。Hadoop 0.19.0中,利用Hadoop fsck /可以根據(jù)HBase打開的日志文件數(shù)目報告DFS的破損程度。 結(jié)論是,在Hadoop 0.21.0之前,你非常容易遇到數(shù)據(jù)丟失。在Hadoop 0.21.0之后,你將得到頂尖的系統(tǒng)(有點吹牛啦,譯者注)。 改善計劃在HBase0.21.0中,WAL構(gòu)架會有相當(dāng)大的改進,這里強調(diào)幾點 1. 替換SequenceFile WAL的核心之一就是存儲格式。SequenceFile有不少問題,最大的性能問題就是所有的寫入都是同步的(HBase-2105)。 在HBase 0.20.0中,HFile替換了MapFile,所以,可以考慮一個完全的替換。首先要做的是HBase獨立于底層的文件系統(tǒng)。HBase-2059使log的實現(xiàn)方式可配。 另一個想法是完全換一種序列化方式。HBase-2055提出利用Hadoop的Avro作為底層系統(tǒng)。Avro也可能成為Hadoop新的RPC格式,希望有越來越多的人熟悉它。 2. Append/Sync 頻繁的調(diào)用hflush()會導(dǎo)致系統(tǒng)變慢。之前的測試發(fā)現(xiàn),如果每一個記錄都調(diào)用舊的syncFs(),將大大降低系統(tǒng)的性能。HBase-1939提出一種“組提交”的方法,可以批量flush數(shù)據(jù)。另外,HBase-1944中提出將“延時flush log”最為一個Column Family 的參數(shù)。設(shè)為“true”,將同步改動到log的工作交給新的LogSyncer類和線程。最后,在HBASE-2041中,將flushlogentries 設(shè)置為1, optinallogflushinterval 設(shè)為1000毫秒。.META.每次改變都會被同步,用戶表可以根據(jù)需要類配置。 3.分布式日志切分 之前談過,當(dāng)region需要被重新分配時,切分日志將成為一個問題。一個方法就是在zookeeper中維護一個regions的列表。這個方法至少能保證所有“干凈”的region能被快速地分配。剩下那些需要等待日志被切分的條目。 剩下的問題就是如何更快地切分日志。這里,BigTable有如下的描述: 一種方案是對于每一個新的tablet server(類似regionserver),需要讀取整個日志文件,然后,分配那些需要被修復(fù)的條目。然而,在這種情況下,如果將一個失敗的tablet server重新分配到100臺機器,那么每個日志文件需要讀100次。 改進方案 我們根據(jù)key對log條目進行排序。之后,所有對一個特定tablet的改動都將是連續(xù)的,順序讀可以有效地提高磁盤檢索的效率。為了并行地排序,我們將日志切分為64MB的分片。每個排序進程都將有Master協(xié)調(diào)管理,并在tablet server給出它將從那些日志文件中恢復(fù)時初始化。 這就是他的原理。在HMaster rewrite (HBASE-1816)中,也提到了日志拆分。HBASE-1364中,將日志拆分打包成一個問題。但我覺得會引入更多的需要討論的細(xì)節(jié)。 |
|
|