硬吃一個P0故障,「線上業務」應該如何調優HBase引數?

阿丸發表於2022-03-22

1.背景

由於種種原因,最近將核心業務生產使用的HBase遷移到了雲上的彈性MapReduce(EMR)叢集上,並使用了EMR的HBase元件預設引數配置。

結果在流量高峰期出現了宿主機故障,掛掉了兩個core節點(部署了region server和datanode),大量region rit,花了15分鐘才自動恢復,硬生生吃了一個P0故障。

覆盤的時候發現,由於雲上EMR對hdfs的socket超時引數預設設定了900000(15min),導致了region重新上線讀取故障節點WAL日誌的時候足足等待了15分鐘才去重試下個節點。這樣的自愈時間顯然是不滿足「線上業務」的需求的,需要將這個超時時間調整到60000(1min),實現快速自愈的目的。

因此,結合HBase自身元件特性與 「線上業務」高可用、低抖動 訴求,全面整理了HBase引數調優的最佳實踐。

2.先回顧下HBase基礎架構

這裡只是簡單回顧下整體架構,方便對照各個元件聊一聊需要優化的引數。更詳細內容可以參考我過去整理的《全面認識HBase架構(建議收藏)

2.1 整體架構

從物理結構上,HBase包含了三種型別的server,zookeeper、HMaster、RegionServer,從而形成了一種主從模式的結構。

硬吃一個P0故障,「線上業務」應該如何調優HBase引數?

 

  • RegionServer主要用來服務讀和寫操作。當使用者通過client訪問資料時,client會和HBase RegionServer 進行直接通訊。
  • HMaster主要進行RegionServer的管理、DDL(建立、刪除表)操作等。
  • Zookeeper是HDFS(Hadoop Distributed File System)的一部分,主要用來維持整個叢集的存活,保障了HA,故障自動轉移。
  • 底層的儲存,還是依賴於HDFS的。Hadoop的DataNode儲存了RegionServer所管理的資料,所有HBase的資料都是存在HDFS中的。Hadoop的NameNode維護了所有物理資料塊的metadata。

2.2 RegionServer組成

一個RegionServer執行在一個HDFS的DataNode上,並且擁有以下元件:

硬吃一個P0故障,「線上業務」應該如何調優HBase引數?

 

  • WAL:全稱Write Ahead Log, 屬於分散式系統上的檔案。主要用來儲存還未被持久化到磁碟上的新資料。如果新資料還未持久化,節點發生當機,那麼就可以用WAL來恢復這些資料。
  • BlockCache:是一個讀快取。它儲存了被高頻訪問的資料。當這個快取滿了後,會清除最近最少訪問的資料。
  • MenStore: 是一個寫快取。它儲存了還未被寫入磁碟的資料。它會在寫入磁碟前,對自身資料進行排序,從而保證資料的順序寫入。每個region的每個colum family會有一份對應的memstore。
  • HFiles:按照字典序儲存各個row的鍵值。

3.讀優化

3.1 優化讀/寫記憶體比例

一個RegionServer上有一個BlockCache和N個Memstore,它們的大小之和必須小於HeapSize* 0.8,否則HBase不能啟動,因為仍然要留有一些記憶體保證其它任務的執行。

BlockCache作為讀快取,對於讀的效能比較重要,如果讀比較多,建議記憶體使用1:4的機器,比如:8cpu32g或者16pu64g的機器。

讀多寫少的場景下,可以調高BlockCache的數值,降低Memstore的數值來提高讀場景效能。

核心調整引數如下:

- hfile.block.cache.size = 0.5 ;
- hbase.regionserver.global.memstore.size = 0.3。

3.2 減少HFile數量

因為HBase讀取時沒有命中快取,就需要開啟HFile。如果HFile檔案越多,IO次數就越多,讀取的延遲就越高。

因此,HBase通過compaction機制來合併HFile。

但是,對於「線上業務」來說,白天流量高峰做compact會嚴重影響磁碟IO,造成讀寫毛刺,因此需要對compact限速。

3.3 開啟「短路讀」特性

HBase資料是儲存在HDFS,從HDFS讀取資料需要經過DataNode,開啟Short-Circuit Local Read後,客戶端可以直接讀取本地資料。

假設現有兩個使用者User1和User2,User1擁有訪問HDFS目錄上/appdata/hbase1檔案的許可權,而User2使用者沒有該許可權,但是User2使用者又需要訪問這個檔案,那麼可以藉助UNIX中「檔案描述符傳遞」的機制,可以讓User1使用者開啟檔案得到一個檔案描述符,然後把檔案描述符傳遞給User2使用者,那麼User2使用者就可以讀取檔案裡面的內容了,即使User2使用者沒有許可權。

這種關係對映到HDFS中,可以把DataNode看作User1使用者,客戶端DFSClient看作User2使用者,需要讀取的檔案就是DataNode目錄中的/appdata/hbase1檔案。實現如下圖所示:

硬吃一個P0故障,「線上業務」應該如何調優HBase引數?

 

核心引數如下:

dfs.client.read.shortcircuit = true

3.4 開啟「對衝讀」特性(需要評估磁碟IO)

當我們開啟「短路讀」特性後,優先會通過Short-Circuit Local Read功能嘗試本地讀。但是在某些特殊情況下,有可能會出現因為磁碟問題或者網路問題引起的短時間本地讀取失敗。

為了應對這類問題,HBase實現了「對衝讀」特性Hedged Read。

該機制基本工作原理為:
客戶端發起一個本地讀,一旦一段時間之後還沒有返回,客戶端將會向其他DataNode傳送相同資料的請求。哪一個請求先返回,另一個就會被丟棄。

當然,這個特性顯然會放大磁碟IO的壓力,需要謹慎評估使用。

核心引數如下:(根據實際環境對引數進行調整)

- dfs.client.hedged.read.threadpool.size = 10 //指定有多少執行緒用於服務hedged reads。如果此值設定為0(預設),則hedged reads為disabled狀態
- dfs.client.hedged.read.threshold.millis:預設為500(0.5秒):在spawning 第二個執行緒前,等待的時間。

4.寫優化

4.1 增大MemStore的記憶體

面對「寫多讀少」的場景, 可以考慮調高MemStore 的記憶體佔比,降低BlockCache的記憶體佔比,跟讀優化3.1的思路正好相反。

具體可以根據讀寫比例來評估。

4.2 適當增加HFile產生

本條與3.2並不衝突,需要權衡

資料寫入過程中,MemStore在滿足一定條件時會flush刷寫到磁碟,生成一個HFile檔案。當一個Store下的HFile檔案數量大於某個閾值時,就會引起寫入或更新阻塞。

RS日誌中會有類似 “has too many store files...” 的資訊。當出現這種情況時,需要等待Compaction合併以減少HFile數量,這裡主要是Minor Compaction即小合併。

所以我們儘量調大這個閾值,減少compaction。

核心引數:

hbase.hstore.blockingStoreFiles = 100

如果寫很快,很容易帶來大量的HFile,因為此時HFile合併的速度還沒有寫入的速度快。

需要在業務低峰期做major compaction,充分利用系統資源。如果HFile降低不下來,則需要新增節點。

4.3 適當增大Memstore阻塞倍數

當MemStore大小達到刷寫閾值(
hbase.hregion.memstore.flush.size,預設128M)時,就會flush刷寫到磁碟,這個操作基本沒有阻塞。但當一個Region的所有MemStore大小達到一個阻塞倍數(hbase.hregion.memstore.block.multiplier,預設值為4,即4倍的刷寫閾值 預設4*128=512M)時,就會阻塞該Region所有的更新請求,並強制flush。客戶端可能會丟擲RegionTooBusyException異常。

為了儘量避免寫入阻塞,可以適當調整這兩個引數

核心引數包括:

hbase.hregion.memstore.flush.size = 128
hbase.hregion.memstore.block.multiplier = 4

5.IO優化

HBase利用compaction機制,通過大量的讀延遲毛刺和一定的寫阻塞,來換取整體上的讀取延遲的平穩。

為了綜合權衡 效能 與 穩定性,需要對compation做限速處理。

核心調整引數如下:

- hbase.offpeak.end.hour = 6 //允許不限速compact的結束時間
- hbase.offpeak.start.hour = 22 //允許不限速compact的開始時間
- hbase.hstore.compaction.throughput.higher.bound = 15728640 //限速compact最大為15M
- hbase.hstore.compaction.throughput.lower.bound = 10485760 //限速compact最小為10M
- hbase.hregion.majorcompactio = 0 //關閉定時major compaction
- hbase.regionserver.thread.compaction.large = 1 //compation執行緒
hbase.regionserver.thread.compaction.small = 1//compaction執行緒
hbase.hstore.compaction.max = 3 //一次Minor Compaction最多合併的HFile檔案數

需要注意的是,白天compaction限速,並且關閉了定時major compaction後,可能會導致HFile合併不足,因此,可以考慮外部控制(如java api)定時在夜間做major compaction來減少HFile數量。

6.故障恢復優化

引起RegionServer當機的原因各種各樣,有因為Full GC導致、網路異常導致、官方Bug導致(close wait埠未關閉)以及DataNode異常導致等等。

這些場景下一旦RegionServer發生當機,HBase都會馬上檢測到這種當機,並且在檢測到當機之後會將當機RegionServer上的所有Region重新分配到叢集中其他正常RegionServer上去,再根據HLog進行丟失資料恢復,恢復完成之後就可以對外提供服務,整個過程都是自動完成的,並不需要人工介入。基本原理如下圖所示:

硬吃一個P0故障,「線上業務」應該如何調優HBase引數?

 

當datanode異常時,如果讀取超時設定過大(dfs.client.socket-timeout和dfs.socket.timeout),region無法正常讀取WAL日誌,就會導致恢復耗時增加。

核心引數如下:

dfs.client.socket-timeout = 60000
dfs.datanode.socket.write.timeout = 480000
dfs.socket.timeout = 60000

7.其他優化

7.1 split策略

HBase 2.0.0 以上版本採用的 split 策略是 SteppingSplitPolicy。

SteppingSplitPolicy 在初期 region 數量較少的時候,split 的閾值較低,會比較頻繁地觸發 split。

我們已經給表做了預分割槽,所以可以將split策略設定為固定大小(大小由引數
hbase.hregion.max.filesize 決定)

hbase.regionserver.region.split.policy = org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy

7.2 開啟rsgroup

rsgroup對於擴縮容等運維操作有很大的幫助,可以很好的控制region移動造成的影響。move_servers_rsgroup 命令的 for 迴圈裡會將 region 逐個移動。

hbase.coprocessor.master.classes = org.apache.hadoop.hbase.rsgroup.RSGroupAdminEndpointhbase.master.loadbalancer.class = org.apache.hadoop.hbase.rsgroup.RSGroupBasedLoadBalancer

另外,為了避免rs故障導致的meta表的「重試風暴」,region漂移失敗(異常opening狀態),可以給meta表設定獨立的rsgroup,與業務rsgroup進行隔離。同時,增大meta表的handler數量。

hbase.regionserver.metahandler.count = 400 //建議根據客戶端數量進行評估設定

8、小結

本文從HBase「基礎架構」出發,梳理各個元件、讀寫流程的引數調優,期望能滿足「線上業務」的 高可用、低抖動 的需求。

如果你有其他優化經驗,歡迎留言評論。

 

都看到最後了,原創不易,點個關注,點個贊吧~
文章持續更新,可以微信搜尋「阿丸筆記 」第一時間閱讀,回覆【筆記】獲取Canal、MySQL、HBase、JAVA實戰筆記,回覆【資料】獲取一線大廠面試資料。
知識碎片重新梳理,構建Java知識圖譜:github.com/saigu/JavaK…(歷史文章查閱非常方便)

相關文章