date: 2020-10-26 15:43:00
updated: 2020-10-26 18:45:00
HBase進階
1. 架構
master負責管理多個region server,一個region server裡有多個region。
一個表會劃分多個region,起初只有一個,資料增多,region增大到一定程度會拆分成2個region
一個表最終被儲存在多個region server裡
hmaster掛了不影響讀寫,但是 create table 這種涉及後設資料的操作,如果hmaster掛了就會報錯
一個region裡有多個store,一個store代表一個列族。(region按照行鍵來劃分,所以相當於每一行有幾個列族就有幾個store)
store包括兩部分:記憶體中的memstore和磁碟的storefile。寫資料會先寫到memstore,當資料達到閾值(預設64M)後 region server 會啟動 flushcache 進行將資料寫到 storefile裡,每次形成一個storefile
當storefile檔案的數量增長到一定閾值後,系統會進行合併(minor、major ),在合併過程中會進行版本合併和刪除工作(major),形成更大的storefile
當一個region所有storefile的大小和數量超過一定閾值後,會把當前的region分割為兩個,並由hmaster分配到相應的regionserver,實現負載均衡
客戶端檢索資料,先在memstore找,找不到再去blockcache查詢,找不到再找storefile,即:client->memstore->blockcache->storefile。如果讀到了會把資料放到blockcache裡快取,方便下次讀取
Region是HBase中儲存和負載均衡的最小單元,不同的Region可以分佈在不同的 Region Server上
Region由一個或者多個Store組成,每個store儲存一個columns family
每個Strore又由一個memStore和0至多個StoreFile組成
memstore為寫入快取,blockcache為讀取快取
2. 寫資料
- Client 向 zk 傳送請求,請求 meta 表所有的 regionServer
- zk 返回 regionServer 地址
- Client 獲取到 meta 表,請求對應的 region 所在的 regionServer
- 返回 meta 表資料
- Client 向 regionServer 傳送寫資料請求
- 寫入 wal(write ahead log)
- 將資料寫入 memstore
- regionServer 反饋給 Client 寫入成功
在 0.9 版本(低版本)時,還存在一個 -ROOT- 表,作用是為了避免meta表過大而拆分為多個子表,可以通過 -ROOT- 表來對meta表進行管理
第6步和第7步 具體流程如下:
-
hbase-server-2.3 版本 搜尋 HRegion 類,再搜尋 STEP 1,
doMiniBatchMutate(BatchOperation<?> batchOp)
方法即為寫資料的部分// STEP 1. Try to acquire as many locks as we can and build mini-batch of operations with locked rows => 寫入之前先獲取鎖 // STEP 2. Update mini batch of all operations in progress with LATEST_TIMESTAMP timestamp // We should record the timestamp only after we have acquired the rowLock, // otherwise, newer puts/deletes are not guaranteed to have a newer timestamp => client如果不傳時間戳,會自動獲取伺服器端的時間戳 // STEP 3. Build WAL edit // STEP 4. Append the WALEdits to WAL and sync. // STEP 5. Write back to memStore
-
hbase-server-1.3 版本
doMiniBatchMutate(BatchOperation<?> batchOp)
方法不一樣。也是先Build WAL edit
,寫入日誌,但是並沒有先同步,而且先寫入memstore,在 finally 那裡去判斷 wal log 是否同步成功,如果不成功,回滾 memstore 記錄
寫資料時會先向hlog寫(方便memstore裡的資料丟失後根據hlog恢復,向hlog中寫資料的時候也是優先寫入記憶體,後臺會有一個執行緒定期非同步刷寫資料到hdfs,如果hlog的資料也寫入失敗,那麼資料就會發生丟失)
頻繁的溢寫會導致產生很多的小檔案,因此會進行檔案的合併,檔案在合併的時候有兩種方式,minor和major,minor表示小範圍檔案的合併,major表示將所有的storefile檔案都合併成一個
3. Flush 過程
當寫資料到一定程度之後,會把記憶體中的資料flush到磁碟,配置項在 hbase-default.xml
1. habse.regionserver.global.memstore.size
預設大小為記憶體大小的0.4。當regionserver的所有的memstore的大小超過這個值的時候,會阻塞客戶端的讀寫
2. habse.regionserver.global.memstore.size.lower.limit
預設大小為第一個配置項的大小的0.95。即從這個值開始flush到記憶體,如果寫資料過快,超過flush的速度,導致memstore逐漸變大,達到堆大小的0.4,那麼就會暫停讀寫操作,專注於flush,直到memstore的大小下降
3. hbase.regionserver.optionalcacheflushinterval
預設為1個小時(當前記憶體最後一次編輯時間+1個小時),自動flush到磁碟
4. hbase.hregion.memstore.flush.size
單個region裡memstore大小。預設為128M,超過這個大小就會刷寫
5. hbase.regionserver.max.logs
如果wal的檔案數量達到這個值(預設32),就會刷寫
6. hbase.regionserver.hlog.blocksize
預設HDFS 2.x版本預設的blocksize大小
4. 讀資料
- Client 向 zk 傳送請求,請求 meta 表所有的 regionServer
- zk 返回 regionServer 地址
- Client 獲取到 meta 表,請求對應的 region 所在的 regionServer
- 返回 meta 表資料
- Client 向 regionServer 傳送讀資料請求
- 同時會讀 memstore 和 storefile,如果 storefile 裡有資料,會載入到 blockcache 中,然後把資料做一個合併,取時間戳最大的那條資料返回給client,並且將資料寫入到 blockcache
- 遵循的大體流程是 client->memstore->blockcache->storefile。如果讀到了會把資料放到blockcache裡快取,方便下次讀取
同時去讀記憶體和磁碟,是為了避免磁碟的時間戳大於記憶體的時間戳,即put資料的時候設定了老時間戳
5. compact 合併
memstore不斷flush到磁碟,生成hfile。
minor compactions 會把多個檔案hfile合併為一個大的hfile
major compactions 會把所有hfile合併為一個hfile,預設是7天,但是生產上應該關閉,會非常消耗資源,應在空閒時間手動觸發
compact 會 rewrite hfile to a single storefile 重寫的過程會下載hdfs檔案,然後重新寫入,所以很消耗資源
hbase.hstore.compactionThreshold
是一個store(列族)允許的hfile個數,超過這個個數就會合並
6. region split 切分
region 交給不同伺服器,緩解熱點問題 => hfile 不斷拆分,檔案越來越大,到最後有可能還是會導致熱點問題的存在,因為有的檔案特別大,查的資料都在這個檔案裡 => 建表的時候實現 預分割槽
HBase 預設分割槽規則
memstore.flush.size=128MB
max.store.size=10G
分割槽規則:Min(R^2 * “hbase.hregion.memstore.flush.size”, “hbase.hregion.max.filesize”)
第一次拆分大小為:min(10G,11128M)=128M // 一開始的時候就一個region,當資料量達到128M時,會一分為二,變成2個region,然後會往第二個region裡寫資料,但是第一個不會寫,處於半滿狀態 => 之前分裂的region都不會再被寫入資料,處於半滿狀態
第二次拆分大小為:min(10G,33128M)=1152M
第三次拆分大小為:min(10G,55128M)=3200M
第四次拆分大小為:min(10G,77128M)=6272M
第五次拆分大小為:min(10G,99128M)=10G
第五次拆分大小為:min(10G,1111128M)=10G // 最大是10G
官方建議使用一個列族,避免的問題是:有的列族很多資料,有的列族可能只有幾條數,按照region切分,然後flush到磁碟,可能會產生很多的小檔案