HDFS 實踐 | HDFS 在B站的探索和實踐

語言: CN / TW / HK

上週我們介紹了 大資料排程YARN在B站的落地實踐 (←點選回顧前文),本週嗶哩嗶哩技術給大家帶來的是HDFS在B站的應用與展望。

一、

HDFS 架構介紹

HDFS離線儲存平臺是Hadoop大資料計算的底層架構,在B站應用已經超過5年的時間。經過多年的發展,HDFS儲存平臺目前已經發展成為總儲存資料量近EB級,元資料總量近百億級,NameSpace 數量近20組,節點數量近萬臺,日均吞吐幾十PB資料量的大型分散式檔案儲存系統。

首先我們來介紹一下B站的HDFS離線儲存平臺的總體架構。

圖 1-1 HDFS 總體架構

HDFS離線儲存平臺之前主要有元資料層和資料層,近年來我們引入了HDFS Router 擴充套件了接入層,形成當前的三層架構,如圖1-1所示。

  • 接入層,主要以HDFS Router為主,HDFS Router提供了HDFS的統一元資料檢視,以掛載點的方式,記錄路徑與叢集的對映關係,將使用者對路徑的請求轉發到不同的NameSpace中。

  • 元資料層,主要記錄檔案元資料資訊,管理檔案讀寫操作,是整個HDFS儲存系統的心臟。

  • 資料層,以儲存節點DataNode為主,用於儲存真實的資料。

接下來將介紹我們在接入層、元資料層、資料層的探索與實踐。

二、

接入層

(一)基於MergeFs的元資料快速擴充套件

由於HDFS叢集儲存資料量的迅猛增長,單個NameSpace已經無法滿足元資料量的快速增長,我們在經歷了HDFS 聯邦機制後擴充套件成多NameSpace,滿足了一段時間的需求。但是隨著叢集元資料量的指數級增長,特別是小檔案數量的猛增,HDFS 聯邦機制逐漸無法滿足當時的需求。

為了能夠快速新增NameSpace以及讓新增的NameSpace迅速接入已有的叢集,負載新增的元資料,現有的聯邦機制和社群版本的RBF也無法滿足當前的要求,我們決定在RBF的基礎上,深度定製開發,來解決主節點擴充套件性問題。

當時元資料層的壓力主要來源於3個方面:

  • 存量元資料資料量大,新增檔案數量增長迅猛。

  • 新增NameSpace無法快速進行遷移,遷移效率不足。

  • 大量目錄存在實時寫入,歷史的遷移方式需要停止寫入。

為了解決元資料層擴充套件能力不足的問題,經過調研社群3.0的HDFS Router和業界相關方案後,我們決定在社群3.3版本的HDFS Router 的基礎上,進行定製開發MergeFs來解決叢集元資料層擴充套件效能問題,整體架構如圖2-1所示:

圖 2-1 HDFS MergeFS 整體架構

  • 在社群版本的HDFS Router 基礎上,定製化開發MergeFS支援元資料遷移,MergeFS 支援按一個掛載點配置2個NameSpace,新寫入資料會按規則路由到新增的NameSpace中,但歷史資料仍然可見,通過這種方式,我們能迅速擴張新的NameSpace,緩解老NameSpace的寫入壓力。

  • 建設了NameSpace Balancer工具,能在業務低峰時期自動化的非同步遷移老NameSpace的歷史資料到新擴容的NameSpace中,遷移完成後收歸掉掛載點,最終實現路徑完全遷移到新的NameSpace中。

  • 基於HDFS元倉,不斷分析出增長較快的目錄,用於指導哪些資料需要遷移。

在支援了接入層的MergeFS後,元資料擴張不再成為瓶頸,我們擴容了14組NameSpace,支援了90億左右的元資料總量,遷移了54億左右的元資料。與此同時,整個叢集的元資料層QPS得到了極大的提升,整體QPS從50K/s上漲到177.8K/s,整個資料遷移工作對上層資料計算任務透明,極大的減少了遷移的工作量,遷移工作量從1人/week,下降到0.1人/week。

(二)接入層限流策略

隨著叢集規模的日益增長,叢集任務的讀寫資料量也有數量級的上漲,對叢集元資料層的請求如果不加以限制,往往會造成熱點NameSpace,針對HDFS儲存這種多租戶場景,往往會影響高優先順序任務的執行,因此我們對接入層Router和元資料層NameNode都做了限流策略,來保障資源向高優先順序任務傾斜,如圖2-2所示:

  • 我們在計算引擎側和HDFS Client段通過CallerContext透傳了user和Jobid資訊,首先在Router層基於使用者,JobId,Optype, NameSpace permit負載判斷,對於需要進行限流的請求,會對Client返回StanbyException,客戶端通過指數回退策略在等待一定時間後再進行重試,最大重試次數為30次。

  • 在NameNode上基於客戶端透傳的Job和User資訊配置優先順序,我們啟用了FairCallQueue進行限流,同時採用基於Job資訊的Job-Based Cost Provider取代了預設的User-Based Cost Provider,實現了實現作業粒度的RPC限流。

圖 2-2 HDFS 流量限制

通過上述的限流策略,我們對Hive,Spark計算引擎的動態分割槽任務等這類短時間大量讀寫請求的任務有了很大的防禦能力,從監控上看,Router上單個NameSpace的Permit耗盡的情況出現的概率大大下降。

(三)接入層Quota 策略

儲存資料的迅猛增長,以及資料治理推動耗時耗力,對租戶資料進行Quota限制也是急需解決的問題。由於之前為了提升NameNode RPC效能,我們在NameNode上進行改造跳過了部分Quota計算的邏輯,極大的降低了NameNode在處理存在大量檔案目錄時的持鎖時間。我們設計了基於接入層Router的quota機制如圖2-3所示,將NameNode中Quota計算和校驗的邏輯前置到Router中。Router中快取了來源於HDFS元倉的路徑Quota資訊,目前是T+1更新,實效性不高,因此我們也正在開發基於NameNode Observer節點的準實時Quota功能,如圖2-4所示。

  • 改造NameNode Observer節點,支援無鎖版本的getQuotaUsage介面,獲取路徑Quota資訊對NameNode整體讀寫無影響;

  • Quota Service 聚合分散在多個NameSpace中的路徑,實現分鐘級Quota計算;

  • HDFS Router訪問Quota Service獲取路徑使用的Quota資訊,進行Quota校驗;

  • Quota Service相關資料提供上層資料平臺進行資產管理使用。

圖 2-3 基於HDFS元倉的Quota機制

圖 2-4 近實時的Quota計算服務

(四)基於Router的Staging目錄分流

由於業務的擴張,叢集上並行的任務數量也迅猛增長,各種任務執行的臨時檔案目錄如:Yarn Staing路徑、Spark Staing路徑、Hive scratchdir路徑、Yarn Logs路徑的QPS請求非常大,需要多組NameSpace進行分擔才能承載,我們通過對這些路徑配置HASH_ALL掛載點。

通過一致性HASH實現多組NameSpace之間的QPS負載均衡,將這些目錄隔離在單獨的NameSpace中,減少主叢集NameNode的讀寫壓力。

三、

元資料層

(一)基於RBF的Observer NameNode策略

HDFS 架構中元資料節點NameNode 的實現中存在針對目錄樹的全域性鎖,很容易就達到讀寫請求處理瓶頸。社群3.0版本中提出了一個Observer Read架構,通過讀寫分離的架構,嘗試提升單個NameService的處理能力。為了提升NameNode請求處理能力,我們在社群版本的Observer機制的基礎上也經過了定製化開發,基於最新的RBF框架,實現動態負載讀請求路由,提升NameNode讀寫效率,如圖3-1所示:

  • 基於RBF的Observer 架構,對於HDFS Client和計算引擎完全透明,無需變更Client配置;

  • 計算引擎可以通過callerContext 透傳是否進行Observer讀請求,適配不同的業務;

  • HDFS Router 判斷是否需要進行Observer Read和msync請求,依據具體情況進行Observer Read。

圖 3-1 Observer NameNode 基礎架構

在Observer NameNode功能落地過程中,我們做了許多的工作:

  • 由於主叢集是2.8.4 版本,而Observer功能在3.X版本才開啟,因此我們合併了大量Hadoop 3.x版本中Observer相關的程式碼到當前版本;

  • 為提升MSync RPC穩定性,我們在NameNode上開啟新的9020埠,用於專門處理Msync請求;

  • 新增RequeueRpcCalls監控項,監控NameNode Observer節點請求re-queue情況;

  • 在RBF上支援根據使用者Client透傳的Callercontext資訊判斷是否使用Observer NameNode,便於不同業務劃分。

通過Observer NameNode的擴容,單個NameSpace的處理能力迅速上漲,由於當前只有部分Adhoc查詢接入Observer節點,單組NameSpace Observer節點增加平均4.5k/s左右QPS,Active NameNode 峰值QPS下降40k/s,此外Active NameNode QueueTime 峰值和平均值均有所下降,均值從23ms下降到11ms ,如圖3-2,圖3-3 所示:

圖 3-2 Observer NameNode 釋出前Active NameNode QueueTime

圖 3-3 Observer NameNode 釋出後Active NameNode QueueTime

(二)NameNode動態負載均衡策略

當前叢集由於DataNode節點和NodeManager節點混合部署,而NodeManager上執行的任務對節點負載造成的影響大小不一,HDFS檔案的讀寫受到DataNode節點的負載影響較大,常常有慢讀慢寫的情況發生。為提高HDFS系統的穩定性,我們在NameNode端加以改造,實現動態的負載均衡策略,如圖 3-4所示:

  • 在DataNode端按照固定的時間視窗採集節點負載資訊,包括IO,Load,頻寬,磁碟使用率資訊,通過滑動時間視窗,獲取當前一段時間內負載均值資訊。

  • 依託 DataNode 心跳上報到NameNode節點相關負載資訊,NameNode節點按照設定的權重進行彙總打分。

  • 當Client請求NameNode新增Block時,NameNode每次選擇 Data Node 時會隨機選出2個DataNode節點,根據之前彙總的分數,選擇負載較低的節點。

圖 3-4 NameNode 負載均衡策略

(三)NameNode 刪除保護策略

在管理資料的過程中,由於多次出現數據誤刪的操作,HDFS原生的回收站功能不足,且事後恢復資料的工作非常困難,我們針對刪除操作進行了限制,用於保護叢集中儲存的資料,主要有以下幾點:

  • 在NameNode端配置能刪除的最小層級,如配置為2,則只能刪除目錄層級在2級以上的目錄;

  • 配置可動態重新整理的保護目錄列表,若被刪除的目錄包含在該列表中或是列表中某個目錄的父祖目錄,則禁止刪除;

  • 所有RPC呼叫的刪除操作轉化為移動到回收站操作,且需要經過上述最小層級驗證和保護目錄驗證;

  • 針對回收站的清理操作,只允許超級管理員使用者和NameNode自動觸發的回收站清理;

  • 回收站清理操作在業務低峰時間進行,並對刪除操作進行限流,保護刪除對NameNode RPC耗時影響。

(四)NameNode 大刪除優化策略

由於叢集檔案大小不一,存在部分目錄檔案數量較多,同時開啟了上述介紹的刪除保護策略,每日回收站清理時會有大量的刪除操作。刪除操作持寫鎖時間長,特別是刪除存在大量檔案的目錄時,可能持寫鎖達到分鐘級,造成NameNode無法處理其他讀寫請求,因此我們參照社群實現了大刪除非同步化策略。從圖3-5的火焰圖中可以看出,刪除操作持鎖時間主要由removeBlocks佔據,因此我們做了如下優化措施:

  • 執行刪除操作時,同步從父目錄中刪除,並收集該目錄的block資訊;

  • 異步向DataNode傳送刪除資料的指令,即使NameNode在該過程中意外終止,資料會在下次DataNode塊彙報時刪除。

通過上述方式,有效的減少了大量檔案目錄刪除時持鎖時間,耗時從分鐘級下降到了秒級。

圖 3-5 NameNode delete操作火焰圖

(五)NameNode FsImage並行載入

由於HDFS單NameSpace的元資料總量上升,我們發現生產環境中NameNode節點啟動時間過長(最長約80min),不僅影響運維效率, 還增加了運維重啟過程中的單點風險, 一旦出現宕機等異常情況,服務將長時間不可用。因此,如何加快NameNode重啟速度是我們亟待解決的問題。

經分析發現,NameNode啟動資訊主要包括以下四個階段:

  1. Loading fsimage:載入fsimage,並構建目錄樹,耗時較長。

  2. Loading edits:回放EditLog,耗時一般在亞秒級,對於長達小時級的重啟時間來說,該階段可能忽略不計。

  3. Saving checkpoint:Checkpoint在HDFS-7097 優化了鎖粒度後,可以與Reporting blocks階段並行,且耗時遠小於Reporting blocks階段,因此,該階段不是NameNode重啟的瓶頸, 亦可忽略。

  4. Reporting blocks:隱含 Data Node registering過程,並接受 Data Node BlockReport彙報,耗時最長。

圖 3-6 NameNode Fsimage 構成圖

由於DataNode BlockReport存在一些方法可以進行加速彙報,我們當前只做了載入fsimage的優化加速。我們對fsimage中佔比最大(90%以上)的部分為INodeSection / INodeDirectorySection進行並行(多執行緒)載入改造,如上圖3-6所示:

  • 將Section拆分為多個小Sub-Section,然後更新FileSummary Section,記錄各SubSecton的資訊詳情;

  • 載入時,依據索引啟動並行的任務,去載入SubSection對應的區間資料,來達到並行載入的目的,縮短載入時長。

通過採用FSimage並行載入機制,NameNode啟動時間有了明顯的下降,從大約80min下降到了50min 左右,後續我們也會繼續優化BlockReport時間,進一步降低啟動耗時。

(六)HDFS資料元倉

HDFS元倉是HDFS所有檔案路徑的大小資訊,讀寫資訊和對應表資訊的彙總,基於每日凌晨產出的HDFS檔案映象和HDFS 審計日誌以及Hive元資料資訊,通過一系列ETL任務,生成HDFS 檔案資訊寬表,積累一定時間段資料後,我們可以獲取目錄檔案增長,檔案訪問資訊等資料,用於指導資料治理和資料冷存等業務的開展,具體架構如圖3-7所示:

圖 3-7 HDFS 元倉模型架構

用資料思維指導資料治理,在HDFS元倉搭建完成後,我們和公司內數倉資料治理團隊,SRE團隊一起,多次推動資料治理,建設HDFS水位預警機制,輸出資料使用報表,推動使用者資料自治。

(七)Smart Storage Manager 管理工具

Smart Storage Manager是Intel開源的智慧儲存管理系統,可以智慧的管理HDFS上的資料,包括自動化使用異構儲存,HDFS Cache,小檔案合併,塊級別的EC和資料壓縮等,主要架構如圖3-6所示。我們引入了SSM服務結合HDFS元倉建設了B站自己的HDFS資料管理服務,當前主要用於冷存資料轉換服務。

  • 基於元倉資訊,分析HDFS路徑一定週期內的訪問次數來判斷是否需要冷存,生成相應的SSM規則。

  • SSM Server 根據上述元倉產生的規則自動化進行資料冷備。

  • 後續我們將不斷拓展SSM服務,支援對使用者透明的資料EC策略的等優化措施。

圖 3-8 HDFS Smart Storage Manager 架構

四、

資料層

HDFS 資料層的問題主要是Client寫入的問題,在一個複雜的分散式系統中,熱點的存在始終無法避免,我們所做的就是盡力保障整個HDFS讀寫的穩定性。

(一)HDFS Client寫入慢節點處理策略

HDFS是一個複雜的分散式儲存系統,其中各個節點負載不一,寫入非常容易遇到慢節點問題。為解決此類問題,我們陸續推出了Pipline Recovery策略和FastFailover策略提升寫入的效率。

  • HDFS Client 統計寫入單個packet耗時,同時判斷寫入一個Block資料量,如果寫入Block的資料量低於50%的Block Size,且寫入Packet的耗時超出設定的閾值,則剔除導致寫入慢的DataNode節點

  • HDFS Client 進入 Pipline Recovery 狀態,重新向NameNode申請新的DataNode ,重建pipline,恢復寫入流,如圖4-1所示。

  • 如果寫入Block的資料量高於50%的Block Size時,且寫入Packet的耗時超出設定的閾值,則提前結束當前block,開啟新的block進行寫入,如圖4-2所示。

圖 4-1 HDFS Client Pipeline Recovery 策略

圖 4-2 HDFS Client FastFailover 策略

通過上述的Pipeline Recovery策略和FastFailover策略,HDFS客戶端的讀寫能力有了很大的提升。寫入packet長尾時間從分鐘級下降到秒級,平均耗時(遇到慢節點的情況)從秒級下降到毫秒級如圖4-3、圖4-4所示。目前Pipline Recovery策略觸發次數大約在50次/s,觸發FastFailover策略大約在100次/24H。

圖 4-3 未進行優化措施

圖 4-4 支援Pipeline Recovery策略和FastFailover策略後

(二)HDFS Client FastSwitchRead功能

客戶端訪問HDFS儲存系統讀取檔案時,會訪問NameNode獲取檔案的全部或者部分block列表,每個block上帶有按距離排好序的datanode地址,客戶端拿到block的位置資訊後,對於每個block,選擇距離客戶端最近的datanode,建立連線來讀取當前block的資料。如果連線的DataNode正好屬於慢節點,則極有可能導致讀取檔案速度變慢。因此我們採用以下兩種方式優化HDFS Client讀取資料。

  • 基於時間視窗吞吐量的慢節點切換

每個block包含多個packet,hdfs client在讀取資料的時候,是按照packet來讀取的,因此,我們假設block-1有d1,d2兩個副本,n個packet,同時,我們設定時間視窗為64ms(即當時間視窗超過64ms計算一次吞吐量),初始吞吐量的閾值為4474bytes/ms,如下圖所示:

圖 4-5 基於時間視窗吞吐量的慢節點切換

  • 基於動態調整超時時間的慢節點切換

從上述基於時間視窗吞吐量避免慢節點可知,計算吞吐量有個前提是,當前packet必須讀取完成,而通常會有在一個慢節點上讀一個packet耗時較長(如讀一個packet耗時超過1分鐘)的現象,為了避免這樣情況發生,保證遇到慢節點時切換足夠敏感,我們通過動態調整客戶端與DataNode的 read socket timeout超時引數,來實現慢節點的切換,我們從一個較小的超時時間(128ms)開始配置,每次切換後超時時間*2,最大時間不超過30s。

圖 4-6 基於動態調整超時時間的慢節點切換

(三)DataNode拆鎖優化

  • DataNode offerService執行緒同時負責heartbeat/fbr/ibr多種工作,經過分析Top20慢 Data Node 日誌發現,部分時段processCommand耗時可達分鐘級, 該操作會阻塞其會阻塞ibr過程,導致block彙報延遲,影響close效率,為解決該問題,對processCommand操作進行非同步化處理;

  • DataNode 鎖優化:DataNode Server端資料處理使用的是排它鎖,當存在IO打滿、Load高等情況時,單操作持鎖時間變長,將影響RPC吞吐;為解決該問題, 將排它鎖轉換為讀寫鎖,讓讀操作並行起來,減少整體的持鎖時間, 增加RPC吞吐;

如下圖所示,經過拆鎖優化後,WriteBlockAvgTime 有了很大的提升。

圖 4-7 升級前DataNode RPC處理均值

圖 4-8 升級後DataNode RPC處理均值

五、

多機房專項

隨著叢集規模的不斷擴大,同時由於單個機房機位達到機房上限無法提供更多的機位用於部署HDFS儲存節點,而單純在異地機房部署 DataNode 節點會帶來無法預計的頻寬問題。因此為了叢集的持續發展,以及跨機房網路的頻寬瓶頸和網路抖動問題,我們設計並建設了HDFS多機房體系。

  • 在異地機房部署相同的HDFS和YARN叢集。

  • 通過Router掛載點資訊確定資料儲存機房。

  • 在任務排程服務上配置DataManager服務,用於判斷任務排程機房資訊。

  • 任務統一提交到RMProxy根據任務/使用者/佇列等mapping關係自動路由到相應機房的Yarn叢集。

  • 公共依賴資料由Transfer服務拷貝映象資料到異地機房,並做TTL管理。

  • 任務跨機房資料讀寫統一接入限流服務,保障跨機房頻寬。

圖 5-1 多機房體系架構

整個跨機房體系聯動了許多部門,整體架構較複雜,涉及到排程,儲存,計算引擎和工具側改造,在這就不多做贅述了,後續我們會針對跨機房專案單獨分享一篇文章介紹。

六、

未來規劃

(一)對使用者透明的EC策略

由於總體資料量的膨脹,對總體儲存的消耗也與日俱增,為了響應公司整體降本增效的戰略,我們準備對大量資料進行EC處理。EC即ErasureCoding,通過對儲存資料進行編碼,能有效降低總儲存量,提升儲存的穩健性。為了儘快開展EC工作,並且做到對各種使用者的讀寫方式透明,我們預期做到以下幾點:

  • 開發EC Data Proxy Sever(Datanode端實現) 相容老版本客戶端讀EC資料。

  • 合併 EC相關Patch 到當前使用的HDFS 客戶端。

  • 單獨部署3.x版本HDFS叢集儲存EC資料。

  • HDFS Router研發支援EC掛載點,EC資料讀寫對使用者透明。

  • 開發對使用者透明的自動化EC轉換工具,資料校驗工具。

(二)冷熱溫三級資料路由策略

由於資料熱點的存在(如部分公共Hive維表等)經常導致DataNode節點整體IO打高,影響該DataNode上其他的資料讀寫受阻,我們計劃引入Alluxio元件,支援熱點資料快取,在Router改造掛載點,使其支援掛載點冷熱溫三級資料型別,自動化路由到對應的目錄。同時支援HDFS元倉自動化分析資料訪問熱度,資料按訪問熱度自動降級為冷存或EC資料。

(三)NameNode拆鎖

隨著元資料層的擴充套件,我們當前已經擴充套件到十幾組的規模。但元資料層的擴容不可能是無限制的,同時各組NameNode的qps請求不均衡,非常出現熱點NameSpace的存在,因此對NameNode進行優化的工作也是必不可少的。考慮到NameNode鎖機制,寫操作會鎖住整個目錄樹,我們計劃對NameNode目錄鎖進行優化,提升NameNode讀寫能力。

(四)Hadoop 3.x版本升級

為了支援HDFS的EC策略,我們計劃新建3.x版本HDFS叢集,用於儲存EC資料,由於3.x版本集成了很多HDFS相關優化,在讀寫相容的情況下,我們計劃用3.x版本慢慢替換掉現有的2.x版本的HDFS叢集。