HBase 讀寫數據流程
Hbase 讀數據流程
-
首先從 zk 找到 meta 表的 region 位置,然後讀取 meta 表中的數據,meta 表中存儲了用戶表的 region 信息
-
根據要查詢的 namespace、表名和 rowkey 信息,找到寫入數據對應的 region 信息
-
找到這個 region 對應的 regionServer,然後發送請求
-
查找對應的 region
-
先從 Memstore 查找數據,如果沒有,再從 BlockCache 上讀取。
Hbase 上 RegionServer 的內存分爲兩部分:
- 一部分作爲 Memstore,主要用來寫
- 另一部分作爲 BlockCache,主要用來讀數據
-
如果 BlockCache 中也沒有,再到 StoreFile 上進行讀取。從 StoreFile 中讀到數據後,不是直接把結果返回給客戶端,而是先把數據寫入 BlockCache,然後再返回給客戶端。
HBase 寫數據流程
- 首先從 zk 找到 meta 表的 region 位置,然後讀取 meta 表中的數據,meta 表中存儲了用戶表的 region 信息。
- 根據 namespace、表名和 rowkey 等信息,找到寫入數據對應的 region
- 找到這個 region 對應的 regionServer,然後發送寫入請求
- 把數據分別寫入 HLog(Write ahead log)和 memStore
- memStore 達到閾值後把數據刷到磁盤,生成 storeFile
- 刪除 HLog 中的歷史數據
HBase 的 flush(刷寫)和 compact(合併)機制
Flush 機制
- 當 memstore 的大小超過這個值的時候,會 flush 到磁盤,默認爲 128M
<property>
<name>hbase.hregion.memstore.flush.size</name>
<value>134217728</value>
</property>
- 當 memstore 的數據超過 1 小時,會 flush 到磁盤
<property>
<name>hbase.regionserver.optionalcacheflushinterval</name>
<value>3600000</value>
</property
- HregionServer 的全局 memstore 的大小,超過該大小會觸發 flush 到磁盤的操作,默認是堆大小的 40%
<property>
<name>hbase.regionserver.global.memstore.size</name>
<value>0.4</value>
</property>
- 手動 flush
flush tableName
阻塞機制
以上介紹了數據刷寫磁盤的標準,但是 HBase 是週期性的檢查是否滿足來進行刷寫的,如果在下次檢查到來之前,數據瘋狂寫入 Memstore,就會把內存撐爆。那怎麼處理這種問題呢?
HBase 有阻塞機制,如果觸發,就無法繼續寫入數據。
- 當 Memstore 數據達到 512MB
計算公式:hbase.hregion.memstore.flush.size*hbase.hregion.memstore..block.multiplier
- hbase.hregion.memstore.flush.size 刷寫的閥值,默認是 134217728,即 128MB。
- hbase.hregion.memstore.block.multiplier 是一個倍數,默認是 4。
- RegionServer 全部 memstore 達到規定值
有時候集羣的“寫負載”非常高,寫入量一直超過 flush 的量,這時,我們就希望 memstore 不要超過一定的安全設置。在這種情況下,寫操作就要被阻塞一直到 memstore 恢復到一個“可管理”的大小, 這個大小就是默認值是堆大小 _ 0.4 _ 0.95。
- hbase.regionserver.global.memstore.size.lower.limit 是 0.95
- hbase.regionserver.global.memstore.size 是 0.4
Compact 合併機制
在 HBase 中主要存在兩種類型的 compact 合併。
- Minor compact 小合併
將 Store 中的多個 HFile(StoreFile)合併爲一個 HFile
這個過程中,刪除和更新的數據僅僅只是做了標記,並沒有物理移除,這種合併的觸發頻率很高。
minor compact 文件選擇標準由以下幾個參數共同決定:
<!--待合併文件數據必須大於等於下面這個值-->
<property>
<name>hbase.hstore.compaction.min</name>
<value>3</value>
</property>
<!--待合併文件數據必須小於等於下面這個值-->
<property>
<name>hbase.hstore.compaction.max</name>
<value>10</value>
</property>
<!--默認值爲128m,
表示文件大小小於該值的store file 一定會加入到minor compaction的store file中
-->
<property>
<name>hbase.hstore.compaction.min.size</name>
<value>134217728</value>
</property>
<!--默認值爲LONG.MAX_VALUE,
表示文件大小大於該值的store file 一定會被minor compaction排除-->
<property>
<name>hbase.hstore.compaction.max.size</name>
<value>9223372036854775807</value>
</property>
觸發條件
- memstore flush
在進行 memstore flush 前後會進行判斷是否觸發 compact
- 定期檢查線程
週期性檢查是否需要進行 compaction 操作,週期性時間由參數:hbase.server.thread.wakefrequency 決定,默認值是 10000 millseconds。
- major compact 大合併
合併 Store 中所有的 HFile 爲一個 HFile。
這個過程中有刪除標記的數據會真正被移除,同時超過單元格 maxVersion 的版本記錄也會被刪除。合併頻率比較低,默認 7 天執行一次,並且性能消耗非常大。建議生產關閉。在應用空閒時間手動觸發。這樣可以防止出現在業務高峯期進行 compact
觸發條件
<!--默認值爲7天進行一次大合併,-->
<property>
<name>hbase.hregion.majorcompaction</name>
<value>604800000</value>
</property>
手動觸發
major_compact tableName
Region 拆分機制
當 Region 中存儲的是大量的 rowkey 數據,當 Region 中的數據條數過多的時候,直接影響查詢效率。當 Region 過大的時候,HBase 就會拆分 Region。
拆分策略
HBase 的 Region Split 策略一共有以下幾種:
- ConstantSizeRegionSplitPolicy
- 0.94 版本前默認切分策略
當 region 大小大於某個閾值(hbase.hregion.max.filesize=10G)之後就會觸發切分,一個 region 等分爲 2 個 region。
但是在生產線上這種切分策略卻有相當大的弊端:切分策略對於大表和小表沒有明顯的區分。閾值(hbase.hregion.max.filesize)設置較大對大表比較友好,但是小表就有可能不會觸
發分裂,極端情況下可能就 1 個,這對業務來說並不是什麼好事。如果設置較小則對小表友好,但一個大表就會在整個集羣產生大量的 region,這對於集羣的管理、資源使用、failover 來
說都不是一件好事
- IncreasingToUpperBoundRegionSplitPolicy
- 0.94 版本~2.0 版本默認切分策略
切分策略稍微有點複雜,總體看和 ConstantSizeRegionSplitPolicy 思路相同,一個 region 大小大於設置閾值就會觸發切分。但是這個閾值並不像 ConstantSizeRegionSplitPolicy 是一個固定的值,而是會在一定條件下不斷調整,調整規則和 region 所屬表在當前 regionserver 上的 region 個數有關係.
region split 的計算公式是:
regioncount^3 _ 128M _ 2,當 region 達到該 size 的時候進行 split:
例如::
第一次 split:1^3 _ 256 = 256MB:
第二次 split:2^3 _ 256 = 2048MB:
第三次 split:3^3 _ 256 = 6912MB:
第四次 split:4^3 _ 256 = 16384MB > 10GB,因此取較小的值 10GB:
後面每次 split 的 size 都是 10GB 了
- SteppingSplitPolicy
- 2.0 版本默認切分策略
這種切分策略的切分閾值又發生了變化,相比 IncreasingToUpperBoundRegionSplitPolicy 簡單了一些,依然和待分裂 region 所屬表在當前 regionserver 上的 region 個數有關係,如果 region 個數等於 1,切分閾值爲 flushsize*2,否則爲 MaxRegionFileSize。
這種切分策略對於大集羣中的大表、小表會比 IncreasingToUpperBoundRegionSplitPolicy 更加友好,小表不會再產生大量的小 region,而是適可而止。
- KeyPrefixRegionSplitPolicy
根據 rowKey 的前綴對數據進行分組,這裏是指定 rowKey 的前多少位作爲前綴,比如 rowKey 都是 16 位的,指定前 5 位是前綴,那麼前 5 位相同的 rowKey 在進行 regionsplit 的時候會分到相同的 region 中。
- DelimitedKeyPrefixRegionSplitPolicy
保證相同前綴的數據在同一個 region 中,例如 rowKey 的格式爲:userideventtype_eventid,指定的 delimiter 爲,則 split 的的時候會確保 userid 相同的數據在同一個 region 中
- DisabledRegionSplitPolicy
不啓用自動拆分, 需要指定手動拆分
拆分策略的應用
Region 拆分策略可以全局統一配置,也可以爲單獨的表指定拆分策略
- 通過 hbase-site.xml 全局統一配置,也可以爲單獨的表指定拆分策略
<property>
<name>hbase.regionserver.region.split.policy</name>
<value>org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy</value>
</property>
- 通過 Java API 爲單獨的表指定 Region 拆分策略
HTableDescriptor tableDesc = new HTableDescriptor("test1");
tableDesc.setValue(HTableDescriptor.SPLIT_POLICY, IncreasingToUpperBoundRegionSplitPolicy.class.getName());
tableDesc.addFamily(new HColumnDescriptor(Bytes.toBytes("cf1")));
admin.createTable(tableDesc);
- 通過 HBase Shell 爲單個表指定 Region 拆分策略
> create 'test2', {METADATA => {'SPLIT_POLICY' =>
'org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy'}},{NAME => 'cf1'}
HBase 表的預分區
- 爲什麼要預分區?
當一個 table 剛被創建的時候,HBase 默認分配一個 region 給 table。也就是說這個時候,所有的讀寫請求都會訪問同一個 regionServer 的同一個 region 中,這個時候就達不到負載均衡的效果了,集羣中其他的 regionServer 就可能會處於比較空閒的狀態。解決這個問題可以用 pre-splitting。在創建 table 的時候就配置好,生成多個 region。
好處就是:
- 增加數據讀寫效率
- 負載均衡,防止數據傾斜
- 方便集羣容災調度 region
每個 region 維護着 startRow 與 endRowyKey,如果加入的數據符合某個 region 維護的 rowkey 範圍,則該數據交給這個 region 來維護。
- 手動指定預分區
create 'person','info1','info2',SPLITS => ['1000','2000','3000']
也可以把分區規則創建於文件中。
create 'student','info',SPLITS_FILE => '/root/hbase/split.txt'
Region 合併
Region 的合併不是爲了性能,而是出於維護的目的。
Region 合併的方式:
- 通過 Merge 類冷合併
需要先關閉 HBase 集羣。
不需要進入 hbase shell,直接執行:
合併的信息可以從頁面上獲取
hbase org.apache.hadoop.hbase.util.Merge user user,,1662823434957.f971c62e76cdff90ea957c0709099bb5. user,1000,1662823434957.366fc3c5e6237a8993cc0e143109c229.
- 通過 online_merge 熱合並 Region
不需要關閉 HBase 集羣,在線進行合併
與冷合併不同的是,online_merge 的傳參是 Region 的 hash 值,Region 的 hash 值就是 Region 名稱的最後那段在兩個“.”之間的字符串部分。
示例:
merge_region 'c8d1a1b7f709dfcd8b0c574b4121fdca','1d7e67e13a48b67d2d7867ca9717183c'