HBase 工作機制及讀寫流程
標簽: 大數據 hbase HRegionServer Compaction HRegion Split MemStore Flush
HBase
一圖看懂 HBase 架構
HBase 最常用的shell操作
HBase 工作機制及讀寫流程
HBase 工作機制及讀寫流程
前言
本篇博客,為大家梳理 HBase 工作機制及讀寫流程。HBase 工作機制涉及 Master 工作機制和RegionServer 工作機制;讀寫流程涉及Region split分裂機制和compact 合并機制。過程難免枯燥乏味,但結果卻很充實。
HBase 工作機制
HBase 工作機制涉及 Master 工作機制和RegionServer 工作機制。
Master工作機制
Master 的工作機制也分為上線和下線兩種。
Master 上線
HBase集群中可以設置多個Hmaster,真正對外提供服務的只有一個。這是HBase集群 Master 上線 的前提條件。
HBase 高可用集群多個Hmaster設置步驟如下:
- 關閉 hbase: stop-hbase.sh
- 在conf目錄下創建backup-masters文件:寫入本機的hostname
- 將backup-masters文件拷貝至其他節點同目錄
- 不要在非主機上啟動hbase: start-hbase.sh
配置成功結果如下:
Master啟動進行以下步驟:
-
從zookeeper上獲取唯一 一個首先獲得 active master的排他鎖,用來阻止其它master成為真正的Master。
-
掃描zookeeper上的/hbase/rs節點,通過zookeeper集群協調監督的特性(主要通過心跳包機制)獲得當前可用的region server列表。
-
Master和每個region server通信,獲得當前已分配的region和region server的對應關系。
-
Master掃描META表,計算得到當前還未分配的region,將他們放入待分配region列表。
從 Master 的上線過程可以看到,Master 保存的信息全是可以冗余的信息(都可以從系統其它地方(WAL)收集或者計算得到)。因此,一般 HBase 集群中總是有一個 Master 在提供服務,還有一個以上的 Master 在等待時機搶占它的位置。
Master下線
master只維護表和region的元數據,不參與表數據IO的過程,所以master下線短時間內對整個hbase集群幾乎沒有影響。表的數據讀寫還可以正常進行。
Hmaster下線后的影響有無法創建刪除表,無法修改表的schema,無法進行region的負載均衡,無法處理region 上下線,無法進行region的合并(region的split可以正常進行,它只有 RegionServer 參與)。
當Master下線后,啟動Zookeeper的選舉機制,選出新的Master,新的Master上線,執行上線流程。
RegionServer 工作機制
RegionServer 工作機制的前提條件是 Master 使用 ZooKeeper集群 來跟蹤 RegionServer 狀態。
Region 管理
任何時刻,一個Region只能分配給一個RegionServer。具體如下:
- Master記錄了當前有哪些可用的RegionServer。以及當前哪些Region分配給了哪些RegionServer,哪些Region還沒有分配。
- 當需要分配的新的Region,并且有一個RegionServer上有可用空間時,Master就給這個RegionServer發送一個裝載請求,把Region分配給這個RegionServer。
- RegionServer得到請求后,就開始對此Region提供服務。
region server上線
當某個 RegionServer 啟動時,會首先在 ZooKeeper 上的 server 目錄下創建一個代表自己的臨時 ZNode,相當于搶占了server文件的獨占鎖。由于 Master 訂閱了 server 目錄下的變更消息(Watcher),當 server 目錄下的文件出現新增或刪除操作時,Master 可以得到來自 ZooKeeper 的實時通知,并將實時信息和操作記錄到HLog文件中。因此一旦 RegionServer 上線,Master 就能馬上得到消息。
region server下線
當 RegionServer 下線時,它和 ZooKeeper 的會話就會斷開,ZooKeeper 會自動刪除代表這臺 RegionServer 的 ZNode,意味著該臺 RegionServer 自動釋放了 server 文件的獨占鎖(ZNode),也就是說Master 就可以確定這臺 RegionServer 節點掛了。
無論出現哪種情況,RegionServer 都無法繼續為它的 Region 提供服務了,此時,ZooKeeper 會將該臺節點的變化信息反饋給Master,Master收到反饋, 就會將這臺 RegionServer 上的未完成的 Region 任務分配給其它還活著的 RegionServer。
HBase 讀寫流程
HBase 讀流程
HBase meta表
meta表存儲了所有用戶HRegion的位置信息,meta表內記錄一行數據是用戶表一個region的start key 到endkey的范圍。meta表存儲在一個zookeeper 知道的一個regionserver里。
HBase meta表 是HBase 讀流程 尋址的關鍵所在。具體步驟如下圖所示:
如上圖HBase meta 尋址的過程一致,HBase 讀流程具體步驟如下:
-
客戶端通過 ZooKeeper 以及META表找到目標數據所在的 RegionServer(就是數據所在的 RegionServer 的主機地址);
-
客戶端發生查詢請求:聯系 RegionServer 查詢目標數據,然后 RegionServer 定位到目標數據所在的 Region,將信息反饋給RegionServer ;
-
客戶端直接讀取裝載目標數據的 RegionServer讀取 Region的結果數據。
此外, RegionServer 查詢目標數據,會因為相同的cell(RowKey/ColumnFamily/Column相同)可能存在3個不同的位置,即Block Cache,MemStore,HFile中讀取目標數據。具體如下圖所示:
總結如下:
- 從Block Cache中讀取
- 從MemStore中讀取
- 從多個HFile中讀取,用Bloom Filter篩掉明顯不存在所需數據的HFile,Index用于快速定位HFile中的數據塊
HBase 寫流程
Client先訪問zookeeper,找到Meta表,并獲取Meta表元數據。確定當前將要寫入的數據所對應的HRegion和HRegionServer服務器。Client向該HRegionServer服務器發起Put寫入數據請求,在HRegionServer中它首先會將該Put操作寫入WAL日志文件中,以防止數據丟失。
當寫完WAL日志文件后,HRegionServer根據Put中的TableName和RowKey找到對應的HRegion,并根據Column Family找到對應的HStore,并將Put寫入到該HStore的MemStore中。寫入成功,返回給客戶端。
當Memstore達到閾值,會把Memstore中的數據flush到Storefile中。
當Storefile越來越多,達到一定數量時,會觸發Compact合并操作,將多個小文件合并成一個大文件。Storefile越來越大,Region也會越來越大,達到閾值后,會觸發Split操作,變成兩個文件。
flush 機制
HBase flush 機制涉及以下6大點:
-
hbase.regionserver.global.memstore.size
: 默認堆大小的40%
regionServer的全局memstore的大小,超過該大小會觸發flush到磁盤的操作,默認是堆大小的40%,而且regionserver級別的flush會阻塞客戶端讀寫 -
hbase.hregion.memstore.flush.size
:默認128M
單個region里memstore的緩存大小,超過那么整個HRegion就會flush。 -
hbase.regionserver.optionalcacheflushinterval
:默認1h
內存中的文件在自動刷新之前能夠存活的最長時間 -
hbase.regionserver.global.memstore.size.lower.limit
:默認堆大小 * 0.4 * 0.95
有時候集群的“寫負載”非常高,寫入量一直超過flush的量,這時,我們就希望memstore不要超過一定的安全設置。在這種情況下,寫操作就要被阻塞一直到memstore恢復到一個“可管理”的大小,這個大小就是默認值是堆大小 * 0.4 * 0.95,也就是當regionserver級別的flush操作發送后,會阻塞客戶端寫,一直阻塞到整個regionserver級別的memstore的大小為 堆大小 * 0.4 *0.95為止 -
hbase.hregion.preclose.flush.size
:默認為5M
當一個 region 中的 memstore 的大小大于這個值的時候,我們就觸發 了 close,會先運行“pre-flush”操作,清理這個需要關閉的memstore,然后 將這個 region 下線。當一個 region 下線了,我們無法再進行任何寫操作。 如果一個 memstore 很大的時候,flush 操作會消耗很多時間。“pre-flush” 操作意味著在 region 下線之前,會先把 memstore 清空。這樣在最終執行 close 操作的時候,flush 操作會很快。 -
hbase.hstore.compactionThreshold
:默認超過3個
一個store里面允許存的hfile的個數,超過這個個數會被寫到新的一個hfile里面 ,也即是每個region的每個列族對應的memstore在fulsh為hfile的時候,默認情況下當超過3個hfile的時候就會 對這些文件進行合并重寫為一個新文件,設置個數越大可以減少觸發合并的時間,但是每次合并的時間就會越長。
HBase flush 機制過程如下:
Compact合并操作
StoreFile 是只讀的,一旦創建后就不可以再修改。因此 HBase 的更新/修改其實是不斷追加的操作。
HBase 只是增加數據,所有的更新和刪除操作,都是在 Compact 階段做。所以,用戶寫操作只需要進入到內存即可立即返回,從而保證 I/O 的高性能。HBase中Compact分為兩種:Minor Compact和Major Compact。
-
Minor Compact:是指選取一些小的、相鄰的HFile將他們合并成一個更大的StoreFile,在這個過程中不會處理已經Deleted或Expired的Cell。(BigTable中將memtable的數據flush的一個HFile/SSTable稱為一次Minor Compaction)
-
Major Compact:是指將所有的HFile合并成一個HFile,可以手動觸發或者自動觸發,但是會引起性能問題,一般安排在周末。
Region Split操作
Client 寫入數據 --> 存入 MemStore,一直到 MemStore 寫滿 --> Flush 成一個 StoreFile,直至增長到一定閾值 --> 觸發 Compact 合并操作 --> 多個 StoreFile 合并成一個 StoreFile,同時進行版本合并和數據刪除 --> 當 StoreFile 經過 Compact 之后,逐步形成越來越大的 StoreFile --> 單個 StoreFile 大小超過一定閾值后,觸發 Split 操作,把當前 Region Split 成 2 個 Region,原來的 Region 會下線,新 Split 出來的 2 個子 Region 會被 HMaster 分配到相應的 RegionServer 上,使得原先的一個 Region 的壓力被分流到 2 個 Region 上。
Region Split操作中是怎么樣把 Region Split 成 2 個 Region的?什么情況下開始分裂,分裂有哪些方式,可以修改分裂配置嗎?下面我將詳細的介紹這些問題。
Region Split 流程
借鑒HBase:分布式列式NoSQL數據庫的Region Split 流程說到非常好,這里我直接引用介紹如下:
- RegionServer在本地決定分割HRegion,并準備分割。第一步,匯報給zookeeper。
- master獲取zookeeper中的狀態。
- RegionServer在HDFS的父目錄區域目錄下創建一個名為“.splits”的子目錄。
- RegionServer關閉父HRegion,強制刷新緩存,并將該區域標記為本地數據結構中的脫機狀態。此時,來到父區域的客戶端請求將拋出NotServingRegionException異常。客戶端將重試一些備用值。
- RegionServer在.splits目錄下創建Region目錄,為子區域A和B創建必要的數據結構。然后,它分割存儲文件,因為它在父區域中為每個存儲文件創建兩個引用文件。那些引用文件將指向父Region文件。
- RegionServer在HDFS中創建實際的區域目錄,并移動每個子Region的引用文件。
- RegionServer向META表發送請求。將父HRegion設置為.META中的脫機狀態,并添加關于子HRegion的信息。在這時,在META中不會為女兒分配單獨的條目。客戶端會看到父區域是分割的,如果他們掃描.META,但不知道子HRegion,直到他們出現在.META。
- RegionServer并行open子HRegion接受寫入。
- RegionServer將女兒A和B添加到.META。以及它所在地區的信息。此后,客戶可以發現新的地區,并向新的地區發出請求,之前的緩存失效。
- HRegion Server向zookeeper匯報split結束的消息,master進行負載均衡。
- 拆分后,meta表和HDFS仍將包含對父HRegion的引用。當子HRegion進行Compaction時,這些引用信息會被刪除。Master也會定期檢查子HRegion,如果沒有父HRegion中的信息,父HRegion將被刪除。
Region Split的大小上限統一配置有以下兩種方法進行更改默認配置項
-
通過修改全局配置文件
hbase.hregion.max.filesize
和hbase.hregion.memstore.flush.size
:<property> <name>hbase.hregion.max.filesize</name> <value>10 *1024* 1024 * 1024</value> # 默認為10G </property> <property> <name>hbase.hregion.memstore.flush.size</name> <value>128</value> # 默認為128M </property>
-
單獨設置
# 第一步 禁用 TABLE_NAME disable 'TABLE_NAME' # 第二步 單獨設置TABLE_NAME最大文件MAX_FILESIZE alter 'TABLE_NAME',METHOD => 'table_att',MAX_FILESIZE=>'134217728' # 第三步,啟用 TABLE_NAME enable'TABLE_NAME'
Region Split 策略有以下四種方法
1. 最簡單粗暴的策略ConstantsizeRegionSplitPolicy
就是Hbase默認的拆分。
2. 限制不斷增長的文件尺寸分裂策略IncreasingToUpperBoundRegionSplitPolicy
說明:
-
這是HBase 0.94.0 默認regionsplit策略
-
根據根據公式
Math.min(r^2*flushSize,maxFileSize)
確定split的maxFilesize;這里假設flushsize為128M。拆分過程如下:第一次拆分大小為: min(10G,l*1*128M)=128M 第二次拆分大小為: min(10G,3*3*128M)=1152M 第三次拆分大小為: min(10G,5*5*128M)=3200M 第四次拆分大小為: min(10G,7*7*128M)=6272M 第五次拆分大小為: min(10G,9*9*128M)=10G
-
兩種split 拆分方式
- 同鍵(以定義的分隔符左側作為是否同鍵認定)進同區
DelimitedKeyPrefixRegionsplitPolicy.delimiter
,比如"-"為分隔符進行分區 - 同鍵(截取從左向右固定長度的RowKey)進同區
KeyPrefixRegionSplitPolicy.prefix_length
,默認指定長度為16
- 同鍵(以定義的分隔符左側作為是否同鍵認定)進同區
3. 熱點Region Split分裂策略BusyRegionSplitPolicy
說明:
-
熱點 Region 分裂策略:為被高頻訪問的 Region 設計的 Region Split分裂策略
-
請求阻塞率:未達到該阻塞率不分裂,默認為20%
<property> <name>hbase.busy.policy.blockedRequests</name> <value>0.2</value> </property>
-
最小年齡10分鐘:少于該時間不分裂,默認10分鐘
<property> <name>hbase.busy.policy.minAge</name> <value>600000</value> </property>
-
計算是否繁忙的窗口:每隔多久統計一次:默認5分鐘
<property> <name>hbase.busy.policy.aggWindow</name> <value>300000</value> </property>
- 統計前提:當前時間-上次統計時間 >=
hbase.busy.policy.aggWindow
- 計算公式:被阻塞訪問次數/總訪問次數 >
hbase.busy.policy.blockedRequests
- 以上1和2條件成立:當前Region為Busy
- 注意:經常出現繁忙Region可以采用,但不確定因素會存在
- 統計前提:當前時間-上次統計時間 >=
4. 關閉/啟用自動分裂DisabledRegionSplitPolicy
-
預分裂:建表時定義自動拆分
# 語法: hbase org.apache.hadoop.hbase.util.RegionSplitter test_split_table HexStringSplit -c 3 -f test_col # 說明: test_split_table 表名 HexStringSplit 拆分點算法 -c 分區數量 -f 列族名稱
例如:
hbase(main):011:0> scan 'hbase:meta',{STARTROW=>'productinfo',LIMIT=>10}
結果:
-
預分裂:建表時定義手動拆分
create 'test_split2','mycf2',SPLITS=>['aaaa','bbbb','cccc','dddd','eeee','fff']
結果:
-
強制分裂:
split 強制分裂方法的調用方式: split 'tableName' split 'namespace:tableName' split 'regionName' # format:'tableName,startKey,id' split 'tableName','splitKey' split 'regionName','splitKey'
例如:
hbase (main) :015:0>split 'student', 'b'
結果:
推薦方案
(1)用預拆分導入初始數據。
(2)然后用自動拆分來讓HBase來自動管理Region
智能推薦
關于靜態/動態類型檢查 和 不可變包裝的筆記
靜態類型檢查和動態類型檢查 Java是一種靜態類型語言。所有變量的類型在編譯時(在程序運行之前)都是已知的,因此編譯器也可以推導出所有表達式的類型。如果a和b聲明為int,那么編譯器得出結論,a+b也是int。 相比較之下,在像Python這樣的動態類型語言中,這種檢查會被推遲到run-time。 靜態檢查一個bug比動態檢查好,而動態檢查它比根本不沒檢查它好。 靜態檢查的好處就是可在編譯階段發現...
IntelliJ IDEA中創建xml文件
IntelliJ IDEA中創建xml文件 1、file—setting,左上角輸入template, 2、在左側欄找到File And Code Templates 3、中間選中Files 4、點擊+號,添加模板 5、輸入模板名字:Name:mybatis-cfg.xml (name可以自定義) 6、后綴名extension:xml 7、在面板中間輸入內容: 8、把enable li...
CocosCreator內存與性能優化
CocosCreator內存與性能優化 一、內存優化 因為 iOS小游戲和微信共用同一個進程,而微信在連續兩次收到系統內存警告的時候會關閉小游戲并釋放小游戲占用的內存。如果你的小游戲有外網用戶反饋“閃退”,或者你自己測試的時候頻繁出現“該小程序可能導致微信響應變慢被終止”等提示,那么就應該是時候優化你的小游戲內存了! 1、優化雙份紋理(必做!) 在你...
TabLayout+ViewPager實現橫向tab導航欄
效果 實現方案 activity 布局文件 activity_main.xml view_pager_1.xml view_pager_2.xml 完整源代碼 https://gitee.com/cxyzy1/tablayoutDemo...
springboot進階
1.配置文件application.yml 1.1基本語法 k:(空格)v:表示一對鍵值對(空格必須有); 以空格的縮進來控制層級關系;只要是左對齊的一列數據,都是同一個層級的 屬性和值也是大小寫敏感; 1.2值的寫法 1)字面量:普通的值(數字,字符串,布爾) k: v:字面直接來寫; 字符串默認不用加上單引號或者雙引號; “”:雙引號;不會轉義字符串里面的特殊字符;特殊...
猜你喜歡
OpenCV學習筆記(二)——初始Mat類
前言: 在計算機內存中,數字圖像以矩陣的形式存儲和運算,比如,在MatLab中,圖像讀取之后對應一個矩陣,在OpenCV中,同樣也是如此。 在早期的OpenCV1.x版本中,圖像的處理是通過IplImage(該名稱源于Intel的另一個開源庫Intel Image Processing Library ,縮寫成...
ArrayList實現
API https://docs.oracle.com/javase/8/docs/api/ 實現 MyList MyArrayList MyIterator MyArrayListIterator Test...
簡單的定時DDOS攻擊樣本分析
簡單的定時DDOS攻擊樣本分析 本次分析的樣本來自《惡意軟件分析》課后實驗–Lab-7-01中的程序,將樣本載入到DIE內查殼,如下 程序無殼,使用的VC++6.0編寫,看到編譯時間為2010/09/30 說明這個樣本已經很老很老了。將樣本載入到IDA內分析相關功能,來到入口函數Main出,程序首先調用了StartServiceCtrlDispatcherA啟動了一個名為MalServ...
2020-07-28:已知sqrt (2)約等于 1.414,要求不用數學庫,求sqrt (2)精確到小數點后 10 位。
福哥答案2020-07-28: 1.二分法。 2.手算法。 3.牛頓迭代法。基礎是泰勒級數展開法。 4.泰勒級數法。 5.平方根倒數速算法,卡馬克反轉。基礎是牛頓迭代法。 golang代碼如下: 敲命令 go test -v -test.run TestSqrt后,結果如下: 評論...