HBase進階

貓熊小才天發表於2020-10-26

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. 寫資料

  1. Client 向 zk 傳送請求,請求 meta 表所有的 regionServer
  2. zk 返回 regionServer 地址
  3. Client 獲取到 meta 表,請求對應的 region 所在的 regionServer
  4. 返回 meta 表資料
  5. Client 向 regionServer 傳送寫資料請求
  6. 寫入 wal(write ahead log)
  7. 將資料寫入 memstore
  8. regionServer 反饋給 Client 寫入成功

在 0.9 版本(低版本)時,還存在一個 -ROOT- 表,作用是為了避免meta表過大而拆分為多個子表,可以通過 -ROOT- 表來對meta表進行管理

第6步和第7步 具體流程如下:

  • hbase-server-2.3 版本 搜尋 HRegion 類,再搜尋 STEP 1doMiniBatchMutate(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. 讀資料

  1. Client 向 zk 傳送請求,請求 meta 表所有的 regionServer
  2. zk 返回 regionServer 地址
  3. Client 獲取到 meta 表,請求對應的 region 所在的 regionServer
  4. 返回 meta 表資料
  5. Client 向 regionServer 傳送讀資料請求
  6. 同時會讀 memstore 和 storefile,如果 storefile 裡有資料,會載入到 blockcache 中,然後把資料做一個合併,取時間戳最大的那條資料返回給client,並且將資料寫入到 blockcache
  7. 遵循的大體流程是 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到磁碟,可能會產生很多的小檔案