HBase使用一個HDFS中可配置的根目錄,默認設爲「/hbase」。可以使用hadoop fs -lsr /hbase查看目錄結構,文件能夠被分爲兩類,一類位於HBase根目錄下,另外一類位於根目錄中的表目錄下。算法
Flush命令能夠將內存中的數據寫到存儲文件中,不然就必須等插入的數據達到配置的刷寫大小。shell
第一組文件是被HLog實例管理的WAL文件,這些日誌文件被建立在HBase的根目錄下一個名爲.logs的目錄中。對於每一個HRegionServer,日誌目錄中都包含一個對應的子目錄。在每一個子目錄中有多個HLog文件(由於日誌滾動)。一個region服務器的全部region共享同一組HLog文件。安全
因爲HDFS使用內置的append機制來追加寫入日誌文件,因此只有等到文件大小達到一個完整的塊時,文件對用戶纔是可見的—包括hadoop fs -lsr命令。雖然put操做的數據被安全持久化了,但當前寫入日誌文件的數據大小是稍微偏離的。服務器
當全部包含的修改都被持久化到存儲文件中,從而再也不須要日誌文件時,它們會被放到HBase根目錄下的.oldlogs目錄中。當條件知足配置上的閾值會觸發日誌的滾動。在十分鐘後,舊的日誌文件將被master刪除,這是經過hbase.master.logcleaner.ttl屬性設置的。Master多線程
在HBase中,每張表都有本身的目錄,其位於文件系統中HBase根目錄下。每張表目錄包括一個名爲.tabledesc的頂層文件,該文件存儲表對應序列化後的HTableDescriptor。其中包括表和列族的定義,同時其內容也能夠被讀取。app
在每張表的目錄裏面,表模式中每一個列族都有一個單獨的目錄。目錄的名字是一部分region名字的MD5散列值。負載均衡
Region文件的整體結構是異步
/<hbase-root-dir>/<tablename>/<encoded-regionname>/<column-family>/<filename> /apps/hbase/data/data/hbase/namespace/d25b2b8cb0d3d1c538437c1b89b8f8c8/info/7aef732982e04b77b7ee2a0ef9d7adc3
能夠在每一個列族目錄中看到實際的數據文件。工具
Region目錄中也有一個.regioninfo文件,這個文件包含了對應region的HRegionInfo實例序列化後的信息。與.tabledesc文件相似,它能被外部工具用來查看region的相關信息。例如,HBase的hbck工具就用它來檢查並生成元數據表中丟失的條目。oop
可選的.tmp目錄是按需求建立的,它被用來存放臨時文件,例如,一次合併的重寫文件。一旦這個過程完成,這些臨時生成的文件一般會被移到region目錄中。
在WAL回放時,任何未提交的修改都會被寫入到每一個region的一個單獨的文件中。以上是第一步,而後若是日誌拆分過程已經成功完成,這些文件將被自動移動到臨時的recovered.edits目錄中。當region被打開時,region服務器將會看到須要恢復的文件,而且回放其中相應的條目。
一旦region超過了配置中region大小的最大值,region就須要拆分,其會建立一個對應的splits目錄,它被用來臨時存放兩個子region相關的數據。若是拆分過程成功,以後它們會被移動到表目錄中,並造成兩個新的region,每一個region表明原始region的一半。
當看到一個region的目錄中沒有.tmp目錄,這就意味着尚未進行過壓縮操做。若是沒有recovered.edits目錄,這就意味着WAL尚未進行過回放操做。
當一個region裏的存儲文件增加到大於配置的hbase.hregion.max.filesize大小或者在列族層面配置的大小時,region會被一分爲二。這個過程一般很是迅速,由於系統只是爲新region建立了兩個對應的文件,每一個region是原始region的一半。
Region服務器經過在父region中建立splits目錄來完成這個過程。接下來關閉該region,此後這個region再也不接受任何請求。
而後region服務器經過在splits目錄中設立必須的文件結構來準備新的子region(使用多線程),包括新region目錄和參考文件。若是這個過程成功完成,它將把兩個新region目錄移到表目錄中。.META.表中父region的狀態會被更新,以表示其如今拆分的節點和子節點是什麼。以上過程能夠避免父region被意外從新打開。
兩個子region都準備好後,將會被同一個服務器並行打開。打開的過程包括更新.META.表,這樣能夠把兩個region像其餘region同樣做爲可用region列出來。以後這兩個region會上線並開始服務請求。
同時也會初始化爲兩個region並對region中的內容執行合併,合併過程在替換引用文件以前會把父region的存儲文件異步重寫到兩個子region中。以上過程會在子region的.tmp目錄中執行。一旦生成了重寫以後的文件,它們將自動取代引用文件。
最終父region會被清理掉,這意味着它在.META.表中的表項會被移除,而且它在磁盤上全部的文件都會被刪除。最後,master被告知關於拆分的狀況,而且能夠因爲負載均衡而把新region移動到其它region服務器上。
在拆分過程當中,全部的步驟都在ZK中進行跟蹤。這使得在一個服務器失效時,其它進程能夠知道這個region狀態。
存儲文件會被後臺的管理進程仔細地監控起來以確保它們處於控制之下。隨着memstore的刷寫會生成不少磁盤文件。若是文件的數目達到閾值,合併(compaction)過程將把它們合併成數量更少的體積更大的文件。這個過程持續到這些文件中最大的文件超過配置的最大儲存文件大小,此時會觸發一個region拆分。
壓縮合並有兩種,即minor和major。Minor合併負責重寫最後生成的幾個文件到一個更大的文件中。文件數量是由hbase.hstore.compaction.min屬性設置的。它的默認值爲3,而且最小值須要大於或等於2.過大的數字將會延遲minor合併的執行,同時也會增長執行時消耗的資源及執行的時間。
Minor合併能夠處理的最大文件數量默認爲10,用戶能夠經過hbase.hstore.compaction.max來配置。Hbase.hstore.compaction.min.size(默認設置爲region的memstore刷寫大小)和hbase.hstore.compaction.max.size(默認設置爲Long.MAX_VALUE)配置項屬性進一步減小了須要合併的文件列表。任何比最大合併大小大的文件都會被排除在外。最小合併大小的功能稍有不一樣:它是一個閾值,而不是每一個文件的限制。它包括全部小於限制的文件,直到到達每次壓縮容許的總文件數量。
算法使用hbase.hstore.compaction.ratio(默認爲1.2,或者120%)來確保在選擇過程當中包括足夠的文件。通過跟新文件總的存儲文件比較以後,這個比例仍將選擇達到那個值的文件。評估老是按照從老文件到新文件的順序來進行的,這樣能夠確保更老的文件首先被合併。這些屬性的組合容許用戶微調一個minor合併包括文件的數量。
HBase支持的另一種合併是major合併:它們把全部文件壓縮成一個單獨的文件。在執行壓縮檢查時,系統自動決定運行哪一種合併。在memstore被刷寫到磁盤後會觸發檢查,或在shell命令compact、major_compact以後觸發檢查,或是相應API在被調用後觸發檢查,抑或是被一個異步的後臺進程觸發後。Region服務器運行這個進程,而其功能由CompactChecker類實現,它以一個固定的週期觸發檢查,這個週期由hbase.server.thread.wakefrequency參數控制(乘以hbase.server.thread.wakefrequency.multiplier,設爲1000,這樣它的執行頻率不會像其餘基於線程的任務那麼頻繁)。
除非用戶使用shell命令major_compact或者使用majorCompact()這個API,將強制運行major合併,不然服務器會首先檢查上次運行到如今是否達到hbase.hregion.majorcompaction(設爲24小時)指定的時限。Hbase.hregion.majorcompaction.jitter(設爲0.2,即20%)參數會將全部存儲的時間週期分開。若是沒有這個抖動,全部的存儲文件都將在每24小時的同一時間運行一個major合併。
若是尚未達到major合併的執行週期,系統會選擇minor合併執行。基於以上配置屬性,服務器將檢查是否有足夠的文件供minor合併執行,若是有就繼續。
當minor合併包括全部的存儲文件,且全部文件均未達到設置的每次壓縮的最大文件數時,minor合併可能被提高成major合併。