KeyValue中包含了豐富的自我描述信息:
併發
KeyValue是支撐」稀疏矩陣」設計的一個關鍵點:一些Key相同的任意數量的獨立KeyValue就能夠構成一行數據。但這種設計帶來的一個顯而易見的缺點:每個KeyValue所攜帶的自我描述信息,會帶來顯著的數據膨脹。mvc
爲何rowkey不能太長?columnfamily、qualifiter儘可能短?app
包括單行、批量多行。設計
(1) 獲取全部待寫入(更新)行記錄的行鎖日誌
(2) 開始執行寫入(更新)操做server
(3) 寫入完成以後再統一釋放全部行記錄的行鎖blog
Mutil Version Concurrent Control。HBase中MVCC機制實現主要分爲兩步:事務
(1) 爲每個寫(更新)事務分配一個Region級別自增的序列號ip
(2) 爲每個讀請求分配一個已完成的最大寫事務序列號ci
MVCC的精髓是寫入的時候分配遞增版本信息(SequenceId),讀取的時候分配惟一的版本用於讀取可見,比之大的版本不可見。這裏須要注意版本必須遞增,並且版本遞增的範圍必定程度上決定了事務是什麼事務,好比HBase是Region級別的遞增版本,那麼事務就是region級別事務。
非必須狀況,不要使用行鎖。
爲何要有sequenceId?
本質問題:mestore、Hlog中的數據是同一份,他們須要同一個標識。
<logseq#-for-entire-txn>:<-1, 3,
The -1 marker is just a special way of being backward compatible with an old HLog which would have contained a single . -1只是一個標誌, 是爲了對舊版本兼容.
RegionServer會爲每一個Region維護了一個變量oldestUnflushedSequenceId(其實是爲每一個Store,爲了方便講解,此處暫且認爲是Region,不影響原理),表示這個Region最先的還未落盤的seqid ,即這個seqid以前的全部數據都已經落盤。
下圖是flush過程當中oldestUnflushedSequenceId變量變化的示意圖,初始時爲null,假設在某一時刻階段二RegionA(紅色方框)要執行flush,中間HLog中sequenceId爲1~4對應的數據將會落盤,在執行flush以前,HBase會append一個空的Entry到HLog,僅爲獲取下一個sequenceId(5),並將這個sequenceId賦給OldestUnflushedSequenceId-RegionA。如圖中第三階段OldestUnflushedSequenceId-RegionA指向sequenceId爲5的Entry。
場景一中右側HLog還有未落盤的數據(sequenceid=5還未落盤),所以不能刪除;而場景二中右側HLog的全部數據都已經落盤,因此這個HLog理論上就已經能夠被刪除回收。
HLog日誌文件超過閾值,會刪除最老的,根據OldestUnflushedSequenceId,若是該日誌中有數據未寫磁盤,則強制刷寫磁盤,而後將該日誌文件刪除。
理論上來講只須要回放Memstore中沒有落地的數據對應的WALEntry,已經落地數據對應的WALEntry能夠skip。可問題是RegionServer已經宕機了,<region, oldestUnflushedSequenceId> 對應信息確定沒有了,如何是好?想辦法持久化唄,上文分析oldestUnflushedSequenceId變量是flush時產生的一個變量,這個變量徹底能夠以flush的時候以元數據的形式寫到HFile中(代碼見下圖):
到目前爲止,上面全部分析都基於一個事實:hbase中flush操做是region級別操做,即每次執行flush都須要整個region中的全部store全都執行flush。
map<region, map<store, oldestUnflushedSequenceId>>
由HMaster來管理並切分日誌==>RegionServer切分日誌(小文件多)==>RegionServer切分日誌後,直接在Region-Buffer中回放。
在大集羣條件下對於不少大表來講表現很優秀,但並不完美,這種策略下不少小表會在大集羣中產生大量小region,分散在整個集羣中。並且在發生region遷移時也可能會觸發region分裂。
雖然產生2個子Region,但仍是指向原來的HFile文件。只有major compaction時,父region的數據纔會遷移到子region目錄。