HBase運維|一次HBase目錄空間佔用異常的解決之旅

一次HBase目錄空間佔用異常的解決之旅

1. 前言

在詳細描述我們此次遇到的HBase的異常之前,先來簡單介紹一下我們集羣的概況。當前我們使用的HBase的版本是cdh6.3.2-hbase2.1.0,由主備雙集羣同時爲線上提供服務,但正常情況下讀寫都集中在主集羣,只有發生超時等異常時,熔斷器纔會把請求切換至備集羣。主備集羣之間通過replication機制同步數據,主備集羣開啓雙向同步。主集羣的節點數是5,備集羣的節點數是4。集羣的一些核心參數如下:

hbase.regionserver.maxlogs 256
hbase.hregion.max.filesize  20G  
HBase RegionServer 的 Java 堆棧大小(字節)  32G(我們的物理機內存三百多G)  
hbase.bucketcache.size  24G  

我們遇到的異常是,主備集羣HBase的相關目錄數據量極不正常,如下圖所示:主集羣備集羣初看這樣的現象時,我們有點懵,集羣中所有表的數據總和也才十幾個T,但總的HBase目錄竟佔用了HDFS一百多T的資源。曾經也嘗試過直接簡單粗暴地直接移走這些目錄中看似無用的數據,只保留/hbase/data目錄下的文件,非常自然,集羣會直接宕機、region丟失。

2. HBase的目錄結構

在繼續往下探討之前,還是乖乖複習一下HBase各個目錄的說明和作用,此處以我們當前使用的版本爲例。hdfs dfs -ls /hbase 用於查看/hbase的目錄結構,hdfs dfs -du -s -h /hbase/*用於查看/hbase/*各個目錄佔用的大小。一個看起來還挺正常的HBase的目錄結構,如下圖:以下內容節選自範老師的《HBase 原理與實踐》第三章第四小節,/hbase/.hbase-snapshot快照文件存儲目錄。用戶執行snapshot後,相關的snapshot元數據文件存儲在該目錄。/hbase/.tmp臨時文件目錄,主要用於HBase表的創建和刪除操作。表創建時候首先會在tmp目錄下執行,執行成功後再將tmp目錄下的表信息移動到實際表目錄下。表刪除操作會將表目錄移動到tmp目錄下,一定時間之後再將tmp目錄下的文件真正刪除。/hbase/MasterProcWALs存儲Master Procedure過程中的WAL文件。Master Procedure功能主要用於可恢復的分佈式DDL操作。在早期HBase版本中,分佈式DDL操作一旦在執行到中間某個狀態發生宕機等異常的情況是沒有辦法回滾的,這會導致集羣元數據不一致。Master Procedure功能使用WAL記錄DDL執行的中間狀態,在異常發生之後可以通過WAL回放明確定位到中間狀態狀態點,繼續執行後續操作以保證整個DDL操作的完整性。/hbase/WALs存儲集羣中所有RegionServer的HLog日誌文件。對於每一個RegionServer,日誌目錄中都包含一個子目錄。客戶端寫數據到集羣時,如果沒有繞過寫WALs,則數據先追加寫入WALs LOG,再寫入緩存,觸發Flush之後,數據纔會落盤,落盤之後這一部分WALs會標記過期,然後被刪除。如果集羣發生意外宕機,緩存中的數據丟失,重啓後回放WALs,才能保證數據完整。/hbase/archive文件歸檔目錄。這個目錄主要用於以下幾個場景

  • 所有對HFile文件的刪除操作都會將待刪除文件臨時放在該目錄。

  • 進行Snapshot或者升級時使用到的歸檔目錄。

  • Compaction 刪除HFile的時候,也會把舊的HFile移動到這裏。

/hbase/corrupt存儲損壞的HLog文件或者HFile文件。/hbase/data存儲集羣中所有Region的HFile數據。HFile文件在data目錄下的完整路徑如下所示:/hbase/data/namespace/tablename/f569a17359edb2250cdf07964be606a7(由region的表名+Start Key+時間戳產生的hashcode)/family/文件名除此之外,data目錄下還存儲了一些重要的子目錄和子文件。

  • .tabledesc:表描述文件,記錄對應表的基本schema信息。

  • .tmp:臨時目錄,主要用來存儲Flush和Compaction過程中的中間結果。以flush爲例,MemStore中的KV數據落盤形成HFile首先會生成.tmp目錄下,一旦完成再從.tmp目錄移動到對應的實際文件目錄。

  • .regioninfo:Region描述文件。

  • recovered.edits:存儲故障恢復時該Region需要回放的WAL日誌數據。RegionServer宕機之後,該節點上還沒有來得及flush到磁盤的數據需要通過WAL回放恢復,WAL文件首先需要按照Region切分,每個Region擁有對應的WAL數據片段,回放時只需要回放自己的WAL數據片段即可。

/hbase/hbase.id集羣啓動初始化時,創建的集羣唯一id。/hbase/hbase.versionHBase軟件版本文件,代碼靜態版本。/hbase/oldWALsWAL歸檔目錄。一旦一個WAL文件中記錄的所有KV數據確認已經從MemStore持久化到HFile,那麼該WAL文件就會被移到該目錄。

3. 異常排查

出現這樣異常的開始時間點是之前的一次異常scan導致集羣宕機,然後經過了多次驚魂重啓之後,WALs、archive、oldWALs目錄就越來越膨脹,不會自己釋放空間了。直到現在,我們也只是知道HBase目錄佔用異常,跟上幾次的重啓有關,但其根本原因究竟是什麼,依舊不知道哇。該怎麼解決呢,首先來分析下主集羣的目錄。HBase主集羣的WALs和archive倆目錄佔用空間賊大,先來看一下WALs目錄長啥樣,WALs目錄下多了很多-splitting後綴的文件夾,且其文件名中的時間戳全是2020-05-22那天(驚魂重啓的日子)再下一級目錄中的文件呢,文件名中的時間戳也是那天,過期了這麼久的數據沒有被及時清除,一時間也想不明白這是爲啥。數據沒被刪除,一定是哪裏有引用。HBase依靠zookeeper來保持狀態(姑且這樣理解),所以理所當然,我們首先需要排查下zookeeper中HBase的目錄,因爲可能一些過期的ZK的狀態清除時出現異常,沒有及時清理,存在引用,導致HBase的一些文件也沒有及時被清理,所以空間佔用居高不下。[meta-region-server, replication, rs, backup-masters, splitWAL, table-lock, flush-table-proc, master-maintenance, online-snapshot, switch, master, running, balancer, draining, namespace, hbaseid, table]Zookeeper上這麼多的目錄,還真不知道該怎麼看,那怎麼辦!只能一個一個ls看一下,再加上望文生義,大膽猜測。劃重點了,zk中splitWAL這個path看着貌似跟WALs這個目錄有關係呀!ls 一下,是空的!別的地方也沒有找到類似相關的信息。說到這裏,需要插入一個之前我們重啓集羣時遇到的異常。

3.1 一次詭異的HBase重啓異常

當時我們在重啓集羣的過程中,發現Master的進程頻繁退出,其日誌輸出如下:大概意思就是,znode /hbase/splitWAL 不可以被list,咦,這是啥意思,那我手動zk Shell下ls一下試試,果然,報錯:

2017-02-20 12:08:03,999 [myid:] - WARN  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1102] - Session 0x1591b713cefd2b3 for server localhost/127.0.0.1:2181, unexpected error, closing socket connection and attempting reconnect
java.io.IOException: Packet len8854970 is out of range!
at org.apache.zookeeper.ClientCnxnSocket.readLength(ClientCnxnSocket.java:112)
at org.apache.zookeeper.ClientCnxnSocketNIO.doIO(ClientCnxnSocketNIO.java:79)
at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:366)
at org.apache.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1081)

WATCHER::
此處省略若干字

關鍵的報錯點是:java.io.IOException: Packet len8854970 is out of range!就是這個znode下面的數據太多了,沒辦法查看。那咋整,谷歌一波,有現成的解決方案。

vim /opt/cloudera/parcels/CDH/lib/zookeeper/bin/zkCli.sh

# 這是之前的
#$JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
# -cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS \
# org.apache.zookeeper.ZooKeeperMain "$@"

# 修改下 增加這個配置 -Djute.maxbuffer=41943040
"$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
"-Djute.maxbuffer=41943040" \
-cp "$CLASSPATH" $CLIENT_JVMFLAGS $JVMFLAGS \
org.apache.zookeeper.ZooKeeperMain "$@"

修改完配置之後,就可以正常在zk Shell下查看這個znode了,ls一下,呼啦啦打出來一大堆文件名,指向的就是WALs splitting爲後綴的文件。沒有時間細想這些文件究竟有啥用,只知道它影響集羣的啓動,但凡有一點辦法,也不會直接刪除znode/hbase/splitWAL 。刪除完這個znode之後,集羣順利啓動了。

3.2 隱患解除

這次的目錄隱患就是上次異常重啓之後埋下的坑,結合這個啓動異常來看,WALs的-splitting的文件夾信息原先在ZK中存在,不是由HBase自動刪除,而是被我們手動刪除的,所以這些文件一直存在HBase的目錄中。那麼,既然znode早就被幹掉了,這些文件還留着幹啥。這些-splitting後綴的文件被移走後,就能徹底解決WALs的目錄佔用問題嘛?再來看一下正常WALs的目錄大小,也有好幾個T,正常文件夾中的HLog也沒有及時被清理。隨便打開一個正常的WALs的目錄,查看前幾個文件hdfs dfs -ls /|head -10(後幾個文件中的時間戳肯定是最近時間產生的)。果然,前幾個文件中的時間戳有2020-05-22的,WALs大概從那個時間點開始就一直沒被釋放。WALs還跟什麼有關呢?主備同步,主集羣的寫入請求會實時同步到備集羣,設想一種場景,機器重啓的過程中發生異常,replication異常或者後續replication的隊列持續積壓,那麼主集羣的WALs目錄是不是一直不會被釋放。HBase集羣的replication的狀態保持也依賴於zookeeper,所以,我們還得來看zookeeper的znode——replication。ls /hbase/replication/peers正常情況下該node路徑下存儲的是你的peer ID,我們集羣只有一個peer,且ID爲1,那麼這個node下面就是1。但此時,這個路徑下除了1之外,還有額外的路徑。ls /hbase/replication/rs再查看下這些目錄,也會找到一些異常文件的引用。這些路徑的名字聯繫HBase WALs中過期的文件來看,被互相引用,一切貌似都能解釋通了。主備同步發生了異常或積壓,WALs目錄中的HLog一直被ZK引用,其狀態一直得不到更新,所以文件一直存在,並越積越多。我們的解決辦法是,重建peer,儘量不要手動刪,要讓HBase自動清理這些過期的數據。

disable_peer "1"
remove_peer "1"
add_peer '1', CLUSTER_KEY => "ip1:2181,ip2:2181,ip3:2181:/hbase", SERIAL => true

靜候片刻,你會發現WALs的目錄被移到oldWALs目錄下了,-splitting目錄大小總和+oldWALs剛放進來的文件=之前WALs的目錄大小。再等一會,oldWALs目錄也慢慢變小。繼續看archive目錄,其實archive目錄這麼大,我們的第一時間反應也是跟集羣快照有關,因爲之前集羣遷移時,對所有的表都進行過快照備份,且已經存在了好幾個月,好幾個月前的快照未刪,這個目錄中的舊數據肯定把這個空間撐了起來。相似的場景其實我們之前也遇到過,但之前清空所有快照後,收效甚微。但這次清空完所有快照之後,這個目錄確實瘦身明顯。主集羣目錄佔用隱患順利解決,沿着這個思路,備集羣的隱患也順勢而解。重建peer,慢慢恢復正常。

4. 總結下吧

通過熟悉HBase各個目錄的作用,聯繫之前的事故,結合Zookeeper異常目錄的排查,幫助我們解決了HBase的目錄隱患,操作前後集羣的數據沒有明顯變化(region數)。但是,問題發生的根本原因,我們依舊沒有捕捉到,-spliting目錄到底是幹啥的?-spliting目錄直接刪除是否會造成數據丟失?依靠peer重建來解決replication的問題,是否會造成主備集羣不一致呢?(十有八九會不一致,後續可以重新同步下數據)對HBase的掌握依舊僅限於冰山一角,學習之路漫漫,心存敬畏之心。

5. 參考鏈接

  • https://blog.bcmeng.com/post/hbase-hdfs.html

  • HBase原理與實踐




往期文章精選

1. HBase內核優化與吞吐能力建設
2.  HBase Shell 十大花式玩兒法
3.  HBase 讀流程解析與優化的最佳實踐
4.  HBase 寫阻塞觸發機制剖析與參數優化
5. HBase+ElasticSearch二級索引實踐
6.  爲什麼不建議在 HBase 中使用過多的列族
7.  說好不哭,但HBase2.0真的好用到哭
8.  HBase平臺|58同城HBase平臺建設實踐
9.  HBase實踐 | HBase IO優化與高可用建設



本文分享自微信公衆號 - HBase工作筆記(HBase-Notes)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章