HBase運維實踐-聊聊RIT的那點事

分類:技術 時間:2016-09-24

相信長時間運維HBase集群的童鞋肯定都會對RIT(Region-In-Transition,很多參考資料誤解為Region-In-Transaction,需要注意)有一種咬牙切齒的痛恨感,一旦Region處于長時間的RIT就會有些不知所措,至少以前的我就是這樣過來的。正所謂“恐懼來源于未知”,不知所措意味著我們對RIT知之甚少,然而“凡事都有因果,萬事皆有源頭”,處于RIT狀態的Region只是肉眼看到的一個結果,為什么會處于RIT狀態才是問題探索的根本,也是解決問題的關鍵。本文就基于hbase 0.98.9版本對RIT的工作機制以及實現原理進行普及性的介紹,同時在此基礎上通過真實案例講解如何正確合理地處理處于RIT狀態的Region。一方面希望大家能夠更好的了解RIT機制,另一方面希望通過本文的學習之后可以不再’懼怕’RIT,正確認識處于RIT狀態的Region。

Region-In-Trasition機制

從字面意思來看,Region-In-Transition說的是Region變遷機制,實際上是指在一次特定操作行為中Region狀態的變遷,那這里就涉及這么幾個問題:Region存在多少種狀態?HBase有哪些操作會觸發Region狀態變遷?一次正常操作過程中Region狀態變遷的完整流程是怎么樣的?如果Region狀態在變遷的過程中出現異常又會怎么樣?

Region存在多少種狀態?有哪些操作會觸發狀態變遷?

HBase在RegionState類中定義了Region的主要狀態,主要有如下:



上圖中實際上定義了四種會觸發Region狀態變遷的操作以及操作對應的Region狀態。其中特定操作行為通常包括assign、unassign、split以及merge等,而很多其他操作都可以拆成unassign和assign,比如move操作實際上是先unassign再assign;

Region狀態遷移是如何發生的?

這個過程有點類似于狀態機,也是通過事件驅動的。和Region狀態一樣,HBase還定義了很多事件(具體見EventType類)。此處以unassign過程為例說明事件是如何驅動狀態變遷的,見下圖:


上圖所示是Region在close時的狀態變遷圖,其中紅字部分就是發生的各種事件。可見,如果發生M_ZK_REGION_CLOSING事件,Region就會從OPEN狀態遷移到PENDING_CLOSE狀態,而發生RS_ZK_REGION_CLOSING事件,Region會從PENDING_CLOSE狀態遷移到CLOSING狀態,以此類推,發生RS_ZK_REGION_CLOSED事件,Region就會從CLOSING狀態遷移到CLOSED狀態。當然,除了這些事件之外,HBase還定義了很多其他事件,在此就不一一列舉。截至到此,我們知道Region是一個有限狀態機,那這個狀態機是如何正常工作的,HMaster、RegionServer、Zookeeper又在狀態機工作過程中扮演了什么角色,那就接著往下看~

一次正常操作過程中Region狀態變遷的完整流程是怎么樣的?

接下來本節以unassign操作為例對這個流程進行解析:

整個unassign操作是一個比較復雜的過程,涉及HMaster、RegionServer和Zookeeper三個組件:

1. HMaster負責維護Region在整個操作過程中的狀態變化,起到一個樞紐的作用。它有兩個重要的HashMap數據結構,分別為regionStates和regionsInTransition,前者用來存儲整個集群中所有Region及其當時狀態,而后者主要存儲在變遷過程中的Region及其狀態,后者是前者的一個子集,不包含OPEN狀態的Regions;

2. RegionServer負責接收HMaster的指令執行具體unassign操作,實際上就是關閉region操作;

3. Zookeeper負責存儲操作過程中的事件,它有一個路徑為/hbase/region-in-transition的節點。一旦一個Region發生unssign操作,就會在這個節點下生成一個子節點,子節點的內容是一個“事件”經過序列化的字符串,并且Master會監聽在這個子節點上,一旦發生任何事件,Master就會監聽到并更新Region的狀態。

下圖是整個流程示意圖:


1. HMaster先執行事件M_ZK_REGION_CLOSING并更新RegionStates,將該Region的狀態改為PENDING_CLOSE,并在regionsInTransition中插入一條記錄;

2. 發送一條RPC命令給擁有該Region的RegionServer,責令其關閉該Region;

3. RegionServer接收到HMaster發送過來的命令之后,首先生成一個RS_ZK_REGION_CLOSING事件,更新到Zookeeper,Master監聽到ZK節點變動之后更新regionStates,將該Region的狀態改為CLOSING;

4. RegionServer執行真正的Region關閉操作:如果該Region正在執行flush或者compaction,等待操作完成;否則將該Region下的所有Memstore強制flush;

5. 完成之后生成事件RS_ZK_REGION_CLOSED,更新到Zookeeper,Master監聽到ZK節點變動之后更新regionStates,將該Region的狀態改為CLOSED;

到這里,基本上將unssign操作過程中涉及到的Region狀態變遷解釋清楚了,當然,其他諸如assign操作基本類似,在此不再贅述。這里其實還有一個問題,即關于HMaster上所有Region狀態是否需要持久化的問題,剛開始接觸這個問題的時候想想并不需要,這些處于RIT的狀態信息完全可以通過Zookeeper上/region-in-transition的子節點信息構建出來。然而,在閱讀HBase Book的相關章節時,看到如下信息:

于是就充滿了疑惑,一方面Master更新hbase:meta是一個遠程操作,代價相對很大;另一方面Region狀態內存更新和遠程更新保證一致性比較困難;再者,Zookeeper上已經有相應RIT信息,再持久化一份并沒有太大意義。為了對其進行確認,就查閱跟蹤了一下源碼,發現是否持久化取決于一個參數: hbase.assignment.usezk ,默認情況下該參數為true,表示使用zk情況下并不會對Region狀態進行持久化(詳見RegionStateStore類),可見HBase Book的那段說明存在問題,在此特別說明~

如果Region狀態在變遷的過程中出現異常會怎么樣?

再回顧unassign的整個過程就會發現一次完整操作涉及太多流程,任何異常都可能會導致Region處于較長時間的RIT狀態,好在HBase針對常見的異常做了最基本的容錯處理:

1. Master宕機重啟:Master在宕機之后會丟失所有內存中的信息,也包括RIT信息以及Region狀態信息,因此在重啟之后會第一時間重建這些信息。重啟之后會遍歷Zookeeper上/hbase/regions-in-transition節點下的所有子節點,解析所有子節點對應的最后一個‘事件’,解析完成之后一方面借此重建全局的Region狀態,另一方面根據狀態機轉移圖對處于RIT狀態的Region進行處理。比如如果發現當前Region的狀態是PENDING_CLOSE,Master就會再次據此向RegionServer發送’關閉Region’的RPC命令。

2. 其他異常宕機:HBase會在后臺開啟一個線程定期檢查內存中處于RIT中的Region,一旦這些Region處于RIT狀態的時長超過一定的閾值(由參數 hbase.master.assignment.timeoutmonitor.timeout 定義,默認600000ms)就會重新執行unassign或者assign操作。比如如果當前Region的狀態是PENDING_CLOSE,而且處于該狀態的時間超過了600000ms,Master就會重新執行unassign操作,向RegionServer再次發送’關閉Region’的RPC命令。

可見,HBase提供了基本的重試機制,保證在一些短暫異常的情況下能夠通過不斷重試拉起那些處于RIT狀態的Region,進而保證操作的完整性和狀態的一致性。 然而不幸的是,因為各種各樣的原因,很多Region還是會掉入長時間的RIT狀態,甚至是永久的RIT狀態,必須人為干預才能解決,下面一節內容讓我們看看都有哪些常見的場景會導致Region會處于永久RIT狀態,以及遇到這類問題應該如何解決。

永久RIT狀態案例分析

通過RIT機制的了解,其實可以發現處于RIT狀態Region并不是什么怪物,大部分處于RIT狀態的Region都是短暫的,即使在大多數短暫異常的情況下HBase也提供了重試機制保證Region能夠很快恢復正常。然而在一些特別極端的場景下還是會發生一些異常導致部分Region掉入永久的RIT狀態,進而會引起表讀寫阻塞甚至整個集群的讀寫阻塞。下面我們舉兩個相關的案例進行說明:

案例一:Compaction永久阻塞

現象:線上一個集群因為未知原因忽然就卡住了,讀寫完全進不來了;另外還有很多處于PENDING_CLOSE狀態的Region。

分析:集群卡住常見原因無非兩個,一是Memstore總消耗內存大小超過了上限進而觸發RegionServer級別flush,此時系統會阻塞集群執行長時間flush操作;二是storefile數量過多超過設定的上限閾值(參見:hbase.hstore.blockingStoreFiles),此時系統會阻塞所有flush請求而執行compaction。

診斷:

(1)首先查看了各個RegionServer上的Memstore使用大小,并沒有達到設定的upperLimit。

(2)再查看了一下所有RegionServer的storefile數量,瞬間石化了,store數為250的RegionServer上storefile數量竟然達到了1.5w ,很多單個store的storefile都超過了設定閾值100

(3)初步懷疑是因為storefile數量過多引起的,看到這么多storefile的第一反應是手動執行major_compaction,然而所有的compact命令好像都沒有起任何作用

(4)無意中發現所有RegionServer的Compaction任務都是同一張表music_actions的,而且Compaction時間都基本持續了一兩天。到此基本可以確認是因為表music_actions的Compaction任務長時間阻塞,占用了所有的Compaction線程資源,導致集群中所有其他表都無法執行Compaction任務,最后導致StoreFile大量堆積

(5)那為什么會存在PENDING_CLOSE狀態的Region呢?經查看,這些處于PENDING_CLOSE狀態的Region全部來自于表music_actions,進一步診斷確認是由于在執行graceful_stop過程中unassign時遇到Compaction長時間阻塞導致RegionServer無法執行Region關閉(參考上文unassign過程),因而掉入了永久RIT

解決方案:

(1)這個問題中RIT和集群卡住原因都在于music_actions這張表的Compaction阻塞,因此需要定位Compaction阻塞的具體原因。經過一段時間的定位初步懷疑是因為這張表的編碼導致,anyway,具體原因不重要,因為一旦Compaction阻塞,好像是沒辦法通過正常命令解除這種阻塞的。臨時有用的辦法是增大集群的Compaction線程,以期望有更多空閑線程可以處理集群中其他Compaction任務,消化大量堆積的StoreFiles

(2)而永久性消滅這種Compaction阻塞只能先將這張表數據遷移出來,然后將這張表暴力刪除。暴力刪除就是先將HDFS對應文件刪除,再將hbase:meta中該表對應的相關數據清除,最后重啟整個集群即可。這張表刪除之后使用hbck檢查一致性之后,集群Compaction阻塞現象就消失了,集群就完全恢復正常。

案例二:HDFS文件異常

現象:線上集群很多RegionServer短時間內頻頻宕機,有幾個Region處于FAILED_OPEN狀態

分析診斷:

(1)查看系統監控以及RegionServer日志,確認RegionServer頻繁宕機是因為大量CLOSE_WAIT狀態的短連接導致。監控顯示短時間內(4h)CLOSE_WAIT的數量從0增長到6w 。

(2)再查看RegionServer日志查看到如下日志:

2016-07-27 09:42:14,932 [RS_OPEN_REGION-inspur250.photo.163.org,60020,1469581282053-0] ERROR org.apache.hadoop.hbase.regionserver.handler.OpenRegionHandler - Failed open of region=news_user_actions,|u:cfcd208495d565ef66e7dff9f98764da|1462799167|30671473410714402,1469522128310.3b3ae24c65fc5094bc2acfebaa7a56de., starting to roll back the global memstore size.java.io.IOException: java.io.IOException: java.io.FileNotFoundException: File does not exist: /hbase/news_user_actions/b7b3faab86527b88a92f2a248a54d3dc/meta/0f47cda55fa44cf9aa2599079894aed62016-07-27 09:42:14,934 [RS_OPEN_REGION-inspur250.photo.163.org,60020,1469581282053-0] INFO  org.apache.hadoop.hbase.regionserver.handler.OpenRegionHandler - Opening of region {NAME =gt; 'news_user_actions,|u:cfcd208495d565ef66e7dff9f98764da|1462799167|30671473410714402,1469522128310.3b3ae24c65fc5094bc2acfebaa7a56de.', STARTKEY =gt; '|u:cfcd208495d565ef66e7dff9f98764da|1462799167|30671473410714402', ENDKEY =gt; '|u:d0', ENCODED =gt; 3b3ae24c65fc5094bc2acfebaa7a56de,} failed, marking as FAILED_OPEN in ZK

日志顯示,Region‘ 3b3ae24c65fc5094bc2acfebaa7a56de ’打開失敗,因此狀態被設置為FAILED_OPEN,原因初步認為是FileNotFoundException導致,找不到的文件是Region‘ b7b3faab86527b88a92f2a248a54d3dc ’下的一個文件,這兩者之間有什么聯系呢?

(3)使用hbck檢查了一把,得到如下錯誤信息:

ERROR: Found lingering reference file hdfs://mycluster/hbase/news_user_actions/3b3ae24c65fc5094bc2acfebaa7a56de/meta/0f47cda55fa44cf9aa2599079894aed6.b7b3faab86527b88a92f2a248a54d3dc

看到這里就一下恍然大悟,從引用文件可以看出來,Region ‘ 3b3ae24c65fc5094bc2acfebaa7a56de ’是‘ b7b3faab86527b88a92f2a248a54d3dc ’的子Region,熟悉Split過程的童鞋就會知道,父Region分裂成兩個子Region其實并沒有涉及到數據文件的分裂,而是會在子Region的HDFS目錄下生成一個指向父Region目錄的引用文件,直到子Region執行Compaction操作才會將父Region的文件合并過來。

到這里,就可以理解為什么子Region會長時間處于FAILED_OPEN狀態:因為子Region引用了父Region的文件,然而父Region的文件因為未知原因丟失了,所以子Region在打開的時候因為找不到引用文件因而會失敗。而這種異常并不能通過簡單的重試可以解決,所以會長時間掉入RIT狀態。

(4)現在基本可以通過RegionServer日志和hbck日志確定Region處于FAILED_OPEN的原因是因為子Region所引用的父Region的文件丟失導致。那為什么會出現CLOSE_WAIT數量暴漲的問題呢?經確認是因為Region在打開的時候會讀取Region對應HDFS相關文件,但因為引用文件丟失所以讀取失敗,讀取失敗之后系統會不斷重試,每次重試都會同datanode建立短連接,這些短連接因為hbase的bug一直得不到合理處理就會引起CLOSEE_WAIT數量暴漲。

解決方案:刪掉HDFS上所有檢查出來的引用文件即可

案例分析

經過上面兩個案例的講解其實看出得出這么幾點:

1. 永久性掉入RIT狀態其實出現的概率并不高,都是在一些極端情況下才會出現。絕大部分RIT狀態都是暫時的。

2. 一旦掉入永久性RIT狀態,說明一定有根本性的問題原因,只有定位出這些問題才能徹底解決問題

3. 如果Region長時間處于PENDING_CLOSE或者CLOSING狀態,一般是因為RegionServer在關閉Region的時候遇到了長時間Compaction任務或Flush任務,所以如果Region在做類似于Major_Compact的操作時盡量不要執行unassign操作,比如move操作、disable操作等;而如果Region長時間處于FAILED_OPEN狀態,一般是因為HDFS文件出現異常所致,可以通過RegionServer日志以及hbck定位出來

寫在文章最后

RIT在很多運維HBase的人看來是一個很神秘的東西,這是因為RIT很少出現,而一旦出現就很致命,運維起來往往不知所措。本文就希望能夠打破這種神秘感,還原它的真實本性。文章第一部分通過層層遞進的方式介紹了Region-In-Transition機制,第二部分通過生產環境的真實案例分析永久性RIT出現的場景以及應對的方案。希望大家能夠更多的了解RIT,通過不斷的運維實踐最后再也不用懼怕它~~


Tags: HBase

文章來源:http://hbasefly.com/2016/09/08/hbase-rit/


ads
ads

相關文章
ads

相關文章

ad