1. 程式人生 > 其它 >HBase-11-日誌分割 (log splitting)

HBase-11-日誌分割 (log splitting)

1.概述

一個region server上的所有region共用一個Hlog, hlog用來在系統異常down掉,MemStore中大量更新丟失時,對資料進行恢復。

然而,對每個region的更新在hlog裡不是連續的,而是分散在Hlog裡的。Hlog中的每項更新都會記錄該更新所屬的region, HBase要通過在每個region上應用hlog中的更新來恢復資料,因此需要把hlog中的更新按照region分組,這一把hlog中更新日誌分組的過程就稱為log split(日誌分割)。

所謂的log split 是將一個WAL檔案,按照不同的region拆分成為多個檔案,每個檔案裡面只是包含一個region內容。log split發生在啟動一個region server之前。

根據實現的不同,Hbase的日誌切分分為三種模式:

  • Log Splitting :效率極差,需要幾個小時
  • Distributed Log Splitting
  • Distributed Log Replay

2.Log splitting

2.1觸發的時機:

  • 日誌拆分(Log Split)發生在叢集啟動時(由HMaster負責完成)

  • 在region server崩潰時(由ServerShutdownHandler負責完成)

    在日誌拆分時,受影響的region將不可用,直到拆分完成,資料完全重置。

    下圖為日誌拆分的過程:

Step1: rename log dir

將對應的region server的目錄重新命名,這樣是為了確保不會出現如果master認為region server掛掉但是實際上region server還在serve的情況。重新命名為 hbase

.logs/, ,-splitting

Step2: start write threads

啟動多個執行緒來寫(如果存在多個檔案的話也可以使用多個執行緒來讀取),但是事實上這樣效率依然不高,因為存在很多機器空閒。

Step3: read edits from each log file, put edit entries in buffers, writers write edits to edits files.

讀執行緒來進行拆分,將需要write的內容丟給寫執行緒完成。

  • 每個執行緒寫入的檔案為/hbase/<table_name>/<region_id>/recovered.edits/.temp
  • 一旦寫成功之後就會重新命名為/hbase/<table_name>/<region_id>/recovered.edits/,其中sequenceid是最後一條寫入這個file的log對應的unique operation id.

Step4:close writers

關閉寫執行緒以及對應的HDFS檔案。

Step5: 指定新的region server來serve某些region,並且讀取這個region對應的HDFS看是否有恢復檔案,如果存在恢復檔案的話那麼就需要進行replay.

2.2.開啟日誌分割

/opt/mapr/hbase/hbase-/conf/hbase-site.xml

<property>
     <name>hbase.master.distributed.log.splitting</name>
     <value>true</value>
</property>

3.Distributed log splitting

機制非常簡單,就是將所有需要被splitting的WAL分散式並行地來完成。首先將這些檔案全部放在zookeeper上面,然後cluster裡面的機器可以上去認領自己來進行split那個日誌,當然也要考慮這個機器在split日誌的時候自己掛掉的情況。

Hbase master 重啟的時候,可以通過web UI 觀察到hlog splitting task。

  1. Master會將待切分日誌路徑釋出到Zookeeper節點上(/hbase/splitWAL),每個日誌作為一個任務,每個任務都會有對應狀態,起始狀態為TASK_UNASSIGNED

  2. 所有RegionServer啟動之後都註冊在這個節點上等待新任務,一旦Master釋出任務之後,RegionServer就會搶佔該任務

  3. 搶佔任務實際上首先去檢視任務狀態,如果是TASK_UNASSIGNED狀態,說明當前沒有人佔有,此時就去修改該節點狀態為TASK_OWNED。如果修改失敗,說明其他RegionServer也在搶佔,修改成功表明任務搶佔成功。

  4. RegionServer搶佔任務成功之後會分發給相應執行緒處理,如果處理成功,會將該任務對應zk節點狀態修改為TASK_DONE,一旦失敗會修改為TASK_ERR

  5. Master會一直監聽在該ZK節點上,一旦發生狀態修改就會得到通知。任務狀態變更為TASK_ERR的話,Master會重新發布該任務,而變更為TASK_DONE的話,Master會將對應的節點刪除。

下圖是RegionServer搶佔任務以及搶佔任務之後的工作流程:

  1. 假設Master當前釋出了4個任務,即當前需要回放4個日誌檔案,分別為hlog1、hlog2、hlog3和hlog4

  2. RegionServer1搶佔到了hlog1和hlog2日誌,RegionServer2搶佔到了hlog3日誌,RegionServer3搶佔到了hlog4日誌

  3. 以RegionServer1為例,其搶佔到hlog1和hlog2日誌之後會分別分發給兩個HLogSplitter執行緒進行處理,HLogSplitter負責對日誌檔案執行具體的切分,切分思路還是首先讀出日誌中每一個<HLogKey, WALEdit>資料對,根據HLogKey所屬Region寫入不同的Region Buffer

  4. 每個Region Buffer都會有一個對應的寫執行緒,將buffer中的日誌資料寫入hdfs中,寫入路徑為/hbase/table/region2/seqenceid.temp,其中seqenceid是一個日誌中某個region對應的最大sequenceid

  5. 針對某一region回放日誌只需要將該region對應的所有檔案按照sequenceid由小到大依次進行回放即可。

4.Distributed Log Replay

相比Distributed Log Splitting方案,流程上的改動主要有兩點:先重新分配Region,再切分回放HLog;Region重新分配開啟之後狀態設定為recovering,核心在於recovering狀態的Region可以對外提供寫服務,不能提供讀服務,而且不能執行split、merge等操作。

DLR的HLog切分回放基本框架類似於Distributed Log Splitting,但它在分解完HLog為Region-Buffer之後並沒有去寫入小檔案,而是直接去執行回放。這樣設計可以大大減少小檔案的讀寫IO消耗,解決DLS的切身痛點。

果不寫小檔案,很難在分散式環境下對sequenceid進行排序,這裡就有一個問題,不按順序對HLog進行回放會不會出現問題?這個問題可以分為下面兩個層面進行討論:

  1. 不同時間更新的相同rowkey,不按順序回放會不會有問題?比如WAL1中有<rowkey, t1>,WAL2中有<rowkey,t2>,正常情況下應該先回放<rowkey,t1>對應的日誌資料,再回放<rowkey,t2>對應的日誌資料,如果順序顛倒會不會有問題?

    第一眼看到這樣的問題覺得一定不行啊,正常情況下<rowkey,t2>對應的日誌資料才是最後的真正資料,一旦顛倒之後不就變成<rowkey,t1>對應的日誌資料了。

    這裡需要關注更新時間的概念,rowkey回放時,如果寫入時間戳定義為回放時間的話,肯定會有異常的。但是如果回放日誌資料的時候rowkey寫入時間戳被定義為當時rowkey資料寫入日誌的時間的話,就正常了。按照上面例子,順序即使顛倒,先寫<rowkey,t2>再寫<rowkey,t1>,但是寫入資料的時間戳(版本)依然保持不變時t2和t1的話,最大版本資料還是<rowkey,t2>,使用者讀取最新資料依然是<rowkey,t2>,和回放順序並沒有關係。

  2. 相同時間戳更新的相同rowkey,不按順序回放會不會有問題?比如WAL1中有<rowkey, t0>,WAL2中有<rowkey,t0>,正常情況下也應該先回放前者,再回放後者,如果順序顛倒會不會有問題?為什麼同一時間會有多條相同rowkey的寫入更新,而且還在不同的日誌檔案中?大家肯定會有這樣的疑問。問題中‘同一時間’的單位是ms,在很多寫入吞吐量很大的場景下同一毫秒寫入大量資料並不是不可能,那先後寫入兩條相同rowkey的資料也必然可能,至於為什麼在不同檔案,假如剛好第一次更新完rowkey的時候日誌截斷了,第二次更新就會落入下一個日誌。

    那這種情況下前後兩次更新時間戳還一致,顛倒順序就辦法分出哪個版本大了呀!莫慌,不是還有sequenceid~,只要在回放的時候將日誌資料原生sequenceid也一同寫入,不就可以在時間戳相同的情況下根據sequenceid進行判斷了。具體實現只需要寫入一個replay標示(用來表示該資料時回放寫入)和相應的sequenceid,使用者在讀取的時候如果遇到兩個都帶有replay標示而且rowkey、cf、column、時間戳都相同的情況,還需要比較sequenceid就可以分辨出來哪個資料版本更大。

參考:

http://hbase.apache.org/book.html#regionserver.arch

https://blog.csdn.net/chicm/article/details/40952563

https://www.w3cschool.cn/hbase_doc/hbase_doc-sz842qgq.html

轉自:https://zhuanlan.zhihu.com/p/27885715

不要小瞧女程式設計師