目錄
寫原理
讀原理
Flush流程
HFile合并流程
Region拆分流程
資料删除時間
HBase系列:
- HBase系列(一)、資料模型
- HBase系列(二)、架構原理
寫原理

用戶端請求HBase寫請求(PUT,DELETE)流程如下:
- Client 先通路ZK中的/hbase/meta-region-server 這個Znode,擷取 hbase:meta 表所在的RegionServer;
- ZK傳回META表所在的RS01資訊;
- Client 通路META所在的RS01,查詢hbase:meta;
- RS01将HBase中繼資料資訊meta傳回給用戶端;
- Client 将得到的中繼資料資訊進行緩存,之後再有讀取請求時可以免去步驟1~5節約時間和資源;
- Client 向目标表所在的RS02發送寫請求(PUT/DELETE);
- RS02将資料順序追加到WAL(HLog)内;
- RS02将資料寫入對應的Region内的MemStore,資料會在MemStore 進行排序;
- RS02向Client 傳回應答資訊完成寫操作;
- 當滿足Flush政策時,memstore内的資料會被刷寫至HFile内;
讀原理
用戶端請求HBase讀請求(GET)流程如下: 前面1~5步驟和寫請求一樣。
- Client 先通路ZK中的/hbase/meta-region-server 這個Znode,擷取 hbase:meta 表所在的RegionServer;
- ZK傳回META表所在的RS01資訊;
- Client 通路META所在的RS01,查詢hbase:meta;
- RS01将HBase中繼資料資訊meta傳回給用戶端;
- Client 将得到的中繼資料資訊進行緩存,之後再有讀取請求時可以免去步驟1~5節約時間和資源;
- Client 向目标表所在的RS02發送讀請求(GET);
- 從BlockCache(塊緩存),MemStore 和 StoreFile(HFile)中查詢目标資料,然後根據RowKey和時間戳進行資料合并;
- 将從StoreFile(HFile)中讀到的資料緩存到BlockCache内供下次查詢時使用,免去再次讀取檔案的磁盤IO消耗;
- 将合并後的結果傳回給用戶端;
注意:
- 這裡并不是先讀記憶體,如果記憶體沒有該資料再讀磁盤,而是同時查詢記憶體(BlockCache,MemStore)以及磁盤(StoreFile)。這樣做的原因是當相同的rowkey在StoreFile檔案内的資料時間戳大于MemStore中的時間戳時,如果先讀MemStore的話會給用戶端傳回一個舊的資料,是以需要同時讀記憶體和磁盤裡的資料,然後根據rowkey為主鍵,以timestamp為版本進行資料合并;
- 同時,在讀磁盤的時候會将磁盤中的HFile緩存到BlockCache内,這樣的話在下一次通路該HFile時就可以先到BlockCache中拿了,如果BlockCache内沒有該檔案,再去StoreFile中取。這裡不必擔心萬一緩存到BlockCache之後又修改了該rowkey的資料會不會與BlockCache内的資料不一緻,因為當資料被修改之後再次讀取時:
- 如果在此期間沒有觸發flush,該資料會在MemStore内,下次讀取資料時會将BlockCache和MemStore中的該rowkey的資料進行合并;
- 如果在此期間觸發了flush,新的資料會在StoreFile中另一個新的HFile内,此時用戶端再次請求讀該RowKey的資料時會同時通路BlockCache内的包含舊版本資料的HFile以及StoreFile中的新HFile進行資料合并,然後會再将新的HFile緩存至BlockCache;
- 也即是說每次讀HBase時,如果該資料不在記憶體中,都會産生一次磁盤IO,而寫資料是直接追加寫WAL和記憶體,是以HBase是寫比讀更快。
Flush流程
上面多次提到了Flush刷寫機制,表示的就是将記憶體中的MemStore資料持久化到HDFS内,即可以避免記憶體壓力過大又可以保證資料安全,我們知道一個Region内包含多個列族,即在同一個Region内的每個Store表示了不同的列族,而每次Flush都會将MemStore持久化到HDFS内的一個HFile中,是以一個Store的資料最終會落到很多HFile中。
MemStore觸發Flush刷寫的條件如下,基本上就是按時間和容量:
- 當Region内某個MemStore 的大小達到了參數( hbase.hregion.memstore.flush.size)的值(預設值 128M),該Region内所有 MemStore 都會刷寫;
- 當MemStore 的大小達到了公式 [hbase.hregion.memstore.flush.size(預設128MB) * hbase.hregion.memstore.block.multiplier(預設4)] 時,會阻止繼續往該MemStore中寫資料;
- 當 RegionServer 中 MemStore 的總大小達到公式[java_heapsize * hbase.regionserver.global.memstore.size(預設值 0.4)*hbase.regionserver.global.memstore.size.lower.limit(預設值 0.95)] 時,Region會按照其所有MemStore 的大小順序(由大到小)依次進行刷寫。直到 該RegionServer 中所有 MemStore 的總大小減小到上述值以下。
- 當 RegionServer 中 MemStore 的總大小達到公式[java_heapsize * hbase.regionserver.global.memstore.size(預設值 0.4)] 時,會阻止繼續往該RegionServer 中所有MemStore中寫資料;
- 通過配置參數(hbase.regionserver.optionalcacheflushinterval)進行自動刷寫(預設 1 小時);
- 當 WAL 檔案的數量超過最大數量 hbase.regionserver.max.logs,region 會按照時間順序依次進行刷寫,直到 WAL 檔案數量減小到 hbase.regionserver.max.log 以下(該屬性名已經廢棄,現無需手動設定,預設值為 32)。
HFile合并流程
由于MemStore 每次刷寫都會生成一個新的HFile,且同一個字段的不同版本(timestamp)和不同類型(Put/Delete)有可能會分布在不同的 HFile 中,是以查詢時需要周遊很多 HFile檔案。為了減少 HFile 的個數,以及清理掉過期和删除的資料,HBase會對StoreFile 進行Compaction(合并)。
Compaction 分為兩種:
- Minor Compaction:将臨近的若幹個較小的 HFile 合并成一個較大的 HFile,但不會清理過期和删除的資料;minor的過程一般較快,而且IO相對較低。當HFile數量達到參數(minFilesToCompact,預設3個)最低标準時,此指令才會觸發,否則忽略本次操作。當待合并的HFile大小超過參數(maxCompactSize)時會将多的檔案過濾掉不進行合并。
- Major Compaction:将一個 Store 下的所有的 HFile 合并成一個大 HFile,并且會清理掉過期和删除的資料;由于此合并政策影響讀寫性能,是以一般在生産環境中,都會禁用major自動合并操作(參數hbase.hregion.majorcompaction設為0,預設是7天),隻在空閑的時段手動或定時執行;
HBase會将隊列中的HFile 按照檔案年齡排序(older to younger),Minor Compaction總是從older HFile 開始選擇,然後将選擇出來的HFiles進行Minor Compaction:
- 如果該檔案小于hbase.hstore.compaction.min.size(為memestoreFlushSize)則一定會被添加到合并隊列中。
- 如果該檔案大于hbase.hstore.compaction.max.size(Long.MAX_VALUE)則一定會被排除,這個值很大,一般不會有。
- 如果該檔案的大小 小于它後面hbase.hstore.compaction.max(預設為10) 個HFile 的大小之和乘以一個ratio(配置項是hbase.hstore.compaction.ratio,預設為1.2),則該HFile 也将加入到minor compaction 中。如果他後面不足10個檔案,那麼也就是取他後面幾個檔案總和*ratio。
相關參數:
參數名 | 配置項 | 預設值 | 含義 |
---|---|---|---|
minFileToCompact | hbase.hstore.compactionThreshold | 3 | 最低合并HFile數量 |
maxFileToCompact | hbase.hstore.compaction.max | 10 | 最大合并HFile數量 |
minCompactSize | hbase.hstore.compaction.min.size | memstoreFlushSize | 最小合并HFile檔案大小 |
maxCompactSize | hbase.hstore.compaction.max.size | Long.MAX_VALUE | 最大合并HFile檔案大小 |
majorCompaction | hbase.hregion.majorcompaction | 7天 | 自動觸發Major 間隔時間 |
Region拆分流程
預設情況下,每個 Table 起初隻有一個 Region,随着資料的不斷寫入,Region 會越來越大然後自動進行拆分(Split)。剛拆分時,兩個子 Region 都位于目前的 RegionServer,但出于負載均衡的考慮,HMaster 有可能會将某個 Region 轉移給其他的 RegionServer。
Region 拆分時機:
- 當1個region中的某個Store下所有StoreFile的總大小超過hbase.hregion.max.filesize(預設10GB),該 Region 就會進行拆分(0.94 版本之前)。
- 當1個region 中的某個 Store 下所有 StoreFile 的總大小超過公式Min(R^2 * "hbase.hregion.memstore.flush.size(預設128MB)",hbase.hregion.max.filesize(預設10GB)"),該 Region 就會進行拆分,其中 R 為目前 RegionServer 中屬于該 Table 的Region個數(0.94 版本之後)。
可以看出,在新版之後,Region的拆分條件會預設從128MB不斷的變大一直到10GB,這樣就會導緻某個表的不同Region内的資料不均衡的情況,是以在實際應用中都會提前設定預分區以及RowKey優化,避免因自動拆分導緻的資料傾斜的情況。
資料删除時間
當HBase中執行PUT修改相同RowKey的資料或者執行DELETE删除RowKey的資料時,我們可以通過指令檢視到曆史版本,那麼什麼時候這些過期資料和删除資料才會被删除呢?
其實失效資料在Flush和Compact都會被删除,但它們實作的地方不一樣:
- Flush刷寫時:
- Flush将MemStore内的資料進行合并然後去除小于該列族設定的VERSION号以前的資料,将剩餘資料持久化到HFile;
- 當執行DELETE時,Flush操作不會删掉MemStore裡的該删除标記記錄,隻會删掉MemStore内該RowKey的在該删除标記操作的timestamp之前的操作,原因是可能在之前已持久化到HFile實體檔案中也有該RowKey記錄的曆史版本,如果把記憶體裡的删除标記也删除的話,再次查詢就會将實體檔案中的曆史版本查出來,這樣就不對了,是以Flush不會删除MemStore内的删除标記記錄。
- Compact合并時:
- Compact指令會真正将記憶體和磁盤上的檔案進行merge,去除小于該列族設定的VERSION号以前的資料,将剩餘資料合并為一個新的HFile;
希望本文對你有幫助,請點個贊鼓勵一下作者吧~ 謝謝!