乾貨 | 阿里巴巴HBase高可用8年抗戰回憶錄

朱小廝的部落格 發表於 2022-12-08
HBase

乾貨 | 阿里巴巴HBase高可用8年抗戰回憶錄

前言

2011年畢玄和竹莊兩位大神將HBase引入阿里技術體系,2014年接力棒轉到東8區第一位HBase commiter天梧手中,多年來與淘寶、旺旺、菜鳥、支付寶、高德、大文娛、阿里媽媽等幾乎全BU合作伙伴攜手共進,支撐了雙十一大屏、支付寶賬單、支付寶風控、物流詳情等核心業務。2018年雙十一,HBase全天處理請求2.4萬億行,單叢集吞吐達到千萬級別。從一個嬰兒成長為青年,阿里HBase摔過很多次,甚至頭破血流,我們在客戶的信任之下幸運的成長,感激涕零。2017年開始阿里HBase走向公有云,我們有計劃的在逐步將阿里內部的高可用技術提供給外部客戶,目前已經上線了同城主備,將作為我們後續高可用能力發展的一個基礎平臺。本文分四個部分回顧阿里HBase在高可用方面的發展:大叢集、MTTF&MTTR、容災、極致體驗,希望能給大家帶來一些共鳴和思考。歡迎使用阿里雲HBase Serverless開啟大資料學習與測試的新時代

大叢集

一個業務一個叢集在初期很簡便,但隨著業務增多會加重運維負擔,更重要的是無法有效利用資源。首先每一個叢集都要有Zookeeper、Master、NameNode這三種角色,固定的消耗3臺機器。其次有些業務重計算輕儲存,有些業務重儲存輕計算,分離模式無法削峰填谷。因此從2013年開始阿里HBase就走向了大叢集模式,單叢集節點規模達到700+。

隔離性是大叢集的關鍵難題。保障A業務異常流量不會衝擊到B業務,是非常重要的能力,否則使用者可能拒絕大叢集模式。阿里HBase引入了分組概念“group”,其核心思想為:共享儲存、隔離計算

乾貨 | 阿里巴巴HBase高可用8年抗戰回憶錄

如上圖所示,一個叢集內部被劃分成多個分組,一個分組至少包含一臺伺服器,一個伺服器同一時間只能屬於一個分組,但是允許伺服器在分組之間進行轉移,也就是分組本身是可以擴容和縮容的。一張表只能部署在一個分組上,可以轉移表到其它的分組。可以看到,表T1讀寫經過的RegionServer和表T2讀寫經過的RegionServer是完全隔離的,因此在CPU、記憶體上都物理隔離,但是下層使用的HDFS檔案系統是共享的,因此多個業務可以共享一個大的儲存池子,充分提升儲存利用率。開源社群在HBase2.0版本上引入了RegionServerGroup。

壞盤對共享儲存的衝擊:由於HDFS機制上的特點,每一個Block的寫入會隨機選擇3個節點作為Pipeline,如果某一臺機器出現了壞盤,那麼這個壞盤可能出現在多個Pipeline中,造成單點故障全域性抖動。現實場景中就是一塊盤壞,同一時間影響到幾十個客戶給你發資訊打電話!特別如果慢盤、壞盤不及時處理,最終可能導致寫入阻塞。阿里HBase目前規模在1萬+臺機器,每週大概有22次磁碟損壞問題。我們在解決這個問題上做了兩件事,第一是縮短影響時間,對慢盤、壞盤進行監控報警,提供自動化處理平臺。第二是在軟體上規避單點壞盤對系統的影響,在寫HDFS的時候併發的寫三個副本,只要兩個副本成功就算成功,如果第三個副本超時則將其放棄。另外如果系統發現寫WAL異常(副本數少於3)會自動滾動產生一個新的日誌檔案(重新選擇pipeline,大機率規避壞點)。最後HDFS自身在高版本也具備識別壞盤和自動剔除的能力。

客戶端連線對Zookeeper的衝擊:客戶端訪問hbase會和Zookeeper建立長連線,HBase自身的RegionServer也會和Zookeeper建立長連線。大叢集意味著大量業務,大量客戶端的連結,在異常情況下客戶端的連結過多會影響RegionServer與Zookeeper的心跳,導致當機。我們在這裡的應對首先是對單個IP的連結數進行了限制,其次提供了一種分離客戶端與服務端連結的方案 HBASE-20159

MTTF&MTTR

穩定性是生命線,隨著阿里業務的發展,HBase逐步擴大線上場景的支援,對穩定性的要求是一年更比一年高。衡量系統可靠性的常用指標是MTTF(平均失效時間)和MTTR(平均恢復時間)

  • MTTF(mean time to failure)

造成系統失效的來源有:

  • 硬體失效,比如壞盤、網路卡損壞、機器當機等

  • 自身缺陷,一般指程式自身的bug或者效能瓶頸

  • 運維故障,由於不合理的操作導致的故障

  • 服務過載,突發熱點、超大的物件、過濾大量資料的請求

  • 依賴失效,依賴的HDFS、Zookeeper元件出現不可用導致HBase程式退出

下面我介紹一下阿里雲HBase在穩定性上遇到的幾個代表性問題:(注:慢盤、壞盤的問題已經在大叢集一節中涉及,這裡不再重複)

  • 週期性的FGC導致程式退出

在支援菜鳥物流詳情業務的時候,我們發現機器大概每隔兩個月就會abort一次,因為記憶體碎片化問題導致Promotion Fail,進而引發FGC。由於我們使用的記憶體規格比較大,所以一次FGC的停頓時間超過了與Zookeeper的心跳,導致ZK session expired,HBase程式自殺。我們定位問題是由於BlockCache引起的,由於編碼壓縮的存在,記憶體中的block大小是不一致的,快取的換入換出行為會逐步的切割記憶體為非常小的碎片。我們開發了BucketCache,很好的解決了記憶體碎片化的問題,然後進一步發展了SharedBucketCache,使得從BlockCache裡面反序列化出來的物件可以被共享複用,減少執行時物件的建立,從而徹底的解決了FGC的問題。

  • 寫入HDFS失敗導致程式退出

HBase依賴倆大外部元件,Zookeeper和HDFS。Zookeeper從架構設計上就是高可用的,HDFS也支援HA的部署模式。當我們假設一個元件是可靠的,然後基於這個假設去寫程式碼,就會產生隱患。因為這個“可靠的”元件會失效,HBase在處理這種異常時非常暴力,立即執行自殺(因為發生了不可能的事情),寄希望於透過Failover來轉移恢復。有時HDFS可能只是暫時的不可用,比如部分Block沒有上報而進入保護模式,短暫的網路抖動等,如果HBase因此大面積重啟,會把本來10分鐘的影響擴大到小時級別。我們在這個問題上的方案是最佳化異常處理,對於可以規避的問題直接處理掉,對於無法規避的異常進行重試&等待。

  • 併發大查詢導致機器停擺

HBase的大查詢,通常指那些帶有Filter的Scan,在RegionServer端讀取和過濾大量的資料塊。如果讀取的資料經常不在快取,則很容易造成IO過載;如果讀取的資料大多在快取中,則很容易因為解壓、序列化等操作造成CPU過載;總之當有幾十個這樣的大請求併發的在伺服器端執行時,伺服器load會迅速飆升,系統響應變慢甚至表現的像卡住了。這裡我們研發了大請求的監控和限制,當一個請求消耗資源超過一定閾值就會被標記為大請求,日誌會記錄。一個伺服器允許的併發大請求存在上限,如果超過這個上限,後來的大請求就會被限速。如果一個請求在伺服器上執行了很久都沒有結束,但客戶端已經判斷超時,那麼系統會主動中斷掉這個大請求。該功能的上線解決了支付寶賬單系統因為熱點查詢而導致的效能抖動問題。

  • 大分割槽Split緩慢

線上上我們偶爾會遇到某個分割槽的數量在幾十GB到幾個TB,一般都是由於分割槽不合理,然後又在短時間內灌入了大量的資料。這種分割槽不但資料量大,還經常檔案數量超級多,當有讀落在這個分割槽時,一定會是一個大請求,如果不及時分裂成更小的分割槽就會造成嚴重影響。這個分裂的過程非常慢,HBase只能從1個分割槽分裂為2個分割槽,並且要等待執行一輪Compaction才能進行下一輪分裂。假設分割槽大小1TB,那麼分裂成小於10GB的128個分割槽需要分裂7輪,每一輪要執行一次Compaction(讀取1TB資料,寫出1TB資料),而且一個分割槽的Compaction只能由一臺機器執行,所以第一輪最多隻有2臺機器參與,第二輪4臺,第三輪8臺。。。,並且實際中需要人為干預balance。整個過程做下來超過10小時,這還是假設沒有新資料寫入,系統負載正常。面對這個問題我們設計了“級聯分裂”,可以不執行Compaction就進入下一次分裂,先快速的把分割槽拆分完成,然後一把執行Compaction。

前面講的都是點,關於如何解決某個頑疾。導致系統失效的情況是多種多樣的,特別一次故障中可能交叉著多個問題,排查起來異常困難。現代醫學指出醫院應當更多投入預防而不是治療,加強體檢,鼓勵早就醫。早一步也許就是個感冒,晚一步也許就變成了癌症。這也適用於分散式系統,因為系統的複雜性和自愈能力,一些小的問題不會立即造成不可用,比如記憶體洩漏、Compaction積壓、佇列積壓等,但終將在某一刻引發雪崩。應對這種問題,我們提出了“健康診斷”系統,用來預警那些暫時還沒有使系統失效,但明顯超過正常閾值的指標。“健康診斷”系統幫助我們攔截了大量的異常case,也在不停的演進其診斷智慧。

  • MTTR(mean time to repair)

百密終有一疏,系統總是會失效,特別的像當機這種Case是低機率但一定會發生的事件。我們要做的是去容忍,降低影響面,加速恢復時間。HBase是一個可自愈的系統,單個節點當機觸發Failover,由存活的其它節點來接管分割槽服務,在分割槽對外服務之前,必須首先透過回放日誌來保證資料讀寫一致性。整個過程主要包括Split Log、Assign Region、Replay Log三個步驟。hbase的計算節點是0冗餘,所以一個節點當機,其記憶體中的狀態必須全部回放,這個記憶體一般可以認為在10GB~20GB左右。我們假設整個叢集的資料回放能力是 R GB/s,單個節點當機需要恢復 M GB的資料,那麼當機N個節點就需要 M * N / R 秒,這裡表達的一個資訊是:如果R不足夠大,那麼當機越多,恢復時間越不可控,那麼影響R的因素就至關重要,在Split Log、Assign Region、Replay Log三個過程中,通常Split Log、Assign Region的擴充套件性存在問題,核心在於其依賴單點。Split Log是把WAL檔案按分割槽拆分成小的檔案,這個過程中需要建立大量的新檔案,這個工作只能由一臺NameNode來完成,並且其效率也並不高。Assign Region是由HBase Master來管理,同樣是一個單點。阿里HBase在Failover方面的核心最佳化是採用了全新的MTTR2架構,取消了Split Log這一步驟,在Assign Region上也做了優先Meta分割槽、Bulk Assign、超時最佳化等多項最佳化措施,相比社群的Failover效率提升200%以上。

從客戶角度看故障,是2分鐘的流量跌零可怕還是10分鐘的流量下降5%可怕?我想可能是前者。由於客戶端的執行緒池資源有限,HBase的單機當機恢復過程可能造成業務側的流量大跌,因為執行緒都阻塞在訪問異常機器上了,2%的機器不可用造成業務流量下跌90%是很難接受的。我們在客戶端開發了一種Fast Fail的機制,可以主動發現異常伺服器,並快速拒絕發往這個伺服器的請求,從而釋放執行緒資源,不影響其它分割槽伺服器的訪問。專案名稱叫做DeadServerDetective

容災

容災是重大事故下的求生機制,比如地震、海嘯等自然災害造成毀滅性打擊,比如軟體變更等造成完全不可控的恢復時間,比如斷網造成服務癱瘓、恢復時間未知。從現實經驗來看,自然災害在一個人的一生中都難遇到,斷網一般是一個年級別的事件,而軟體變更引發的問題可能是月級別的。軟體變更是對運維能力、核心能力、測試能力等全方位的考驗,變更過程的操作可能出錯,變更的新版本可能存在未知Bug。另一個方面為了不斷滿足業務的需求又需要加速核心迭代,產生更多的變更。

容災的本質是基於隔離的冗餘,要求在資源層面物理隔離、軟體層面版本隔離、運維層面操作隔離等,冗餘的服務之間保持最小的關聯性,在災難發生時至少有一個副本存活。阿里HBase在幾年前開始推進同城主備、異地多活,目前99%的叢集至少有一個備叢集,主備叢集是HBase可以支援線上業務的一個強保障。主備模式下的兩個核心問題是資料複製和流量切換

資料複製

選擇什麼樣的複製方式,是同步複製還是非同步複製,是否要保序?主要取決於業務對系統的需求,有些要求強一致,有些要求session一致,有些可以接受最終一致。佔在HBase的角度上,我們服務的大量業務在災難場景下是可以接受最終一致性的(我們也研發了同步複製機制,但只有極少的場景),因此本文主要專注在非同步複製的討論上。很長一段時間我們採用社群的非同步複製機制(HBase Replication),這是HBase內建的同步機制。

同步延遲的根因定位是第一個難題,因為同步鏈路涉及傳送方、通道、接受方3個部分,排查起來有難度。我們增強了同步相關的監控和報警。

熱點容易引發同步延遲是第二個難題。HBase Replication採用推的方式進行復制,讀取WAL日誌然後進行轉發,傳送執行緒和HBase寫入引擎是在同一臺RegionServer的同一個程式裡。當某臺RegionServer寫入熱點時,就需要更多的傳送能力,但寫入熱點本身就擠佔了更多的系統資源,寫入和同步資源爭搶。阿里HBase做了兩個方面的最佳化,第一提高同步效能,減少單位MB同步的資源消耗;第二研發了遠端消耗器,使其它空閒的機器可以協助熱點機器同步日誌。

資源需求、迭代方式的不匹配是第三個難題。資料複製本身是不需要磁碟IO的,只消耗頻寬和CPU,而HBase對磁碟IO有重要依賴;資料複製的worker本質上是無狀態的,重啟不是問題,可以斷點續傳,而HBase是有狀態的,必須先轉移分割槽再重啟,否則會觸發Failover。一個輕量級的同步元件和重量級的儲存引擎強耦合在一起,同步元件的每一次迭代升級必須同時重啟HBase。一個重啟就可以解決的同步問題,因為同時要重啟hbase而影響線上讀寫。一個擴容CPU或者總頻寬的問題被放大到要擴容hbase整體。

綜上所述,阿里HBase最終將同步元件剝離了出來作為一個獨立的服務來建設,解決了熱點和耦合的問題,在雲上這一服務叫做BDS Replication。隨著異地多活的發展,叢集之間的資料同步關係開始變得複雜,為此我們開發了一個關於拓撲關係和鏈路同步延遲的監控,並且在類環形的拓撲關係中最佳化了資料的重複傳送問題。

乾貨 | 阿里巴巴HBase高可用8年抗戰回憶錄

BDS Replication

流量切換

在具備主備叢集的前提下,災難期間需要快速的把業務流量切換到備份叢集。阿里HBase改造了HBase客戶端,流量的切換髮生在客戶端內部,透過高可用的通道將切換命令傳送給客戶端,客戶端會關閉舊的連結,開啟與備叢集的連結,然後重試請求。

乾貨 | 阿里巴巴HBase高可用8年抗戰回憶錄

阿里雲同城主備

切換瞬間對Meta服務的衝擊:hbase客戶端首次訪問一個分割槽前需要請求Meta服務來獲取分割槽的地址,切換瞬間所有客戶端併發的訪問Meta服務,現實中併發可能在幾十萬甚至更多造成服務過載,請求超時後客戶端又再次重試,造成伺服器一直做無用功,切換一直無法成功。針對這個問題我們改造了Meta表的快取機制,極大地提高了Meta表的吞吐能力,可以應對百萬級別的請求。同時在運維上隔離了Meta分割槽與資料分割槽,防止相互影響。

從一鍵切換走向自動切換。一鍵切換還是要依賴報警系統和人工操作,現實中至少也要分鐘級別才能響應,如果是晚上可能要10分鐘以上。阿里HBase在演進自動切換過程中有兩個思路,最早是透過增加一個第三方仲裁,實時的給每一個系統打健康分數,當系統健康分低於一個閾值,並且其備庫是健康的情況下,自動執行切換命令。這個仲裁系統還是比價複雜的,首先其部署上要保持網路獨立,其次其自身必須是高可靠的,最後健康分的正確性需要保證。仲裁系統的健康判斷是從伺服器視角出發的,但從客戶端角度來講,有些時候伺服器雖然活著但是已經不正常工作了,可能持續的FGC,也可能出現了持續網路抖動。所以第二個思路是在客戶端進行自動切換,客戶端透過失敗率或其它規則來判定可用性,超過一定閾值則執行切換。

極致體驗

在風控和推薦場景下,請求的RT越低,業務在單位時間內可以應用的規則就越多,分析就越準確。要求儲存引擎高併發、低延遲、低毛刺,要高速且平穩的執行。阿里HBase團隊在核心上研發CCSMAP最佳化寫入快取,SharedBucketCache最佳化讀取快取,IndexEncoding最佳化塊內搜尋,加上無鎖佇列、協程、ThreadLocal Counter等等技術,再結合阿里JDK團隊的ZGC垃圾回收演算法,線上上做到了單叢集P999延遲小於15ms。另一個角度上,風控和推薦等場景並不要求強一致,其中有一些資料是離線匯入的只讀資料,所以只要延遲不大,可以接受讀取多個副本。如果主備兩個副本之間請求毛刺是獨立事件,那麼理論上同時訪問主備可以把毛刺率下降一個數量級。我們基於這一點,利用現有的主備架構,研發了DualService,支援客戶端並行的訪問主備叢集。在一般情況下,客戶端優先讀取主庫,如果主庫一定時間沒有響應則併發請求到備庫,然後等待最先返回的請求。DualService的應用獲得的非常大的成功,業務接近零抖動。

主備模式下還存在一些問題。切換的粒度是叢集級別的,切換過程影響大,不能做分割槽級別切換是因為主備分割槽不一致;只能提供最終一致性模型,對於一些業務來講不好寫程式碼邏輯;加上其它因素(索引能力,訪問模型)的推動,阿里HBase團隊基於HBase演進了自研的Lindorm引擎,提供一種內建的雙Zone部署模式,其資料複製採用推拉組合的模式,同步效率大大提升;雙Zone之間的分割槽由GlobalMaster協調,絕大部分時間都是一致的,因此可以實現分割槽級別切換;Lindorm提供強一致、Session一致、最終一致等多級一致性協議,方便使用者實現業務邏輯。目前大部分阿里內部業務已經切換到Lindorm引擎。

零抖動是我們追求的最高境界,但必須認識到導致毛刺的來源可以說無處不在,解決問題的前提是定位問題,對每一個毛刺給出解釋既是使用者的訴求也是能力的體現。阿里HBase開發了全鏈路Trace,從客戶端、網路、伺服器全鏈路監控請求,豐富詳盡的Profiling將請求的路徑、資源訪問、耗時等軌跡進行展示,幫助研發人員快速定位問題。

總結

本文介紹了阿里HBase在高可用上的一些實踐經驗,結尾之處與大家分享一些看可用性建設上的思考,拋磚引玉希望歡迎大家討論。

從設計原則上:

1.面向使用者的可用性設計,在影響面、影響時間、一致性上進行權衡
MTTF和MTTR是一類衡量指標,但這些指標好不一定滿足使用者期望,這些指標是面向系統本身而不是使用者的。

2.面向失敗設計,你所依賴的元件總是會失敗
千萬不要假設你依賴的元件不會失敗,比如你確信HDFS不會丟資料,然後寫了一個狀態機。但實際上如果多個DN同時當機資料就是會丟失,此時可能你的狀態機永遠陷入混亂無法推進。再小機率的事件總是會發生,對中標的使用者來講這就是100%。

從實現過程上:

1.完善的監控體系
監控是基礎保障,是最先需要投入力量的地方。100%涵蓋故障報警,先於使用者發現問題是監控的第一任務。其次監控需要儘可能詳細,資料展示友好,可以極大的提高問題定位能力。
2.基於隔離的冗餘
冗餘是可用性上治本的方法,遇到未知問題,單叢集非常難保障SLA。所以只要不差錢,一定至少來一套主備。
3.精細的資源控制
系統的異常往往是因為資源使用的失控,對CPU、記憶體、IO的精細控制是核心高速穩定執行的關鍵。需要投入大量的研發資源去迭代。
4.系統自我保護能力
在請求過載的情況下,系統應該具備類如Quota這樣的自我保護能力,防止雪崩發生。系統應該能識別一些異常的請求,進行限制或者拒絕。
5.Trace能力
實時跟蹤請求軌跡是排查問題的利器,需要把Profiling做到儘量詳細。

本文作者:daniel.meng

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69940568/viewspace-2661176/,如需轉載,請註明出處,否則將追究法律責任。