趣頭條百PB規模 Hadoop實踐(HDFS篇)

文章背景

趣頭條數據量達到了百PB級別,本文分享一下趣頭條的HDFS相關實踐。

NameNode負載和擴展性問題

拆RPC端口以及拆NameSpace組成Federation

針對NameNode單點瓶頸,在把NameNode拆分成Client RPC端口和Service RPC端口後,推進了HDFS Federation的架構,原因是NameNode單點存在元數據量激增的問題,也存在NameNode RPC負載激增的問題。

針對Federation之間的數據遷移引入FastCopy:
如下圖所示:
在這裏插入圖片描述
針對大數據量的Federation各個NameSpace之間的拷貝,比Distcp提升3倍左右的效率。

Balancer負載轉移和搬遷優化

拆分成Federation架構之後,HDFS Balancer 操作對Active NameNode造成了很大對負載,爲此我們把Balancer操作的負載轉移到了standby上面,從而降低了Active NameNode的RPC負載。
具體把Balancer負載轉移到Standby NameNode思想和社區最新的HDFS讀寫分離思想是一致的,讀寫分離HDFS社區具體的Issue爲:HDFS-12943 ,而對應的Balancer轉移到了ObserverNode的patch爲: HDFS-14162。而我們的版本還不支持讀寫分離的功能,爲了快速降低負載,我們把Balancer對Active NameNode的RPC主動拋異常到了Standby NameNode,並且讓Standby NameNode對Balancer放行。
搬遷的時候忽略小的塊,按照從大到小到順序降序,增加搬遷的速度。
具體如下圖:
在這裏插入圖片描述

拆分日誌相關的NameSpace降低負載

有了HDFS Federation架構以後,日誌還是會和業務的NameSpace互相產生影響,爲此我們把defaultFs修改成系統單獨的NameSpace。我們也向Hadoop YARN社區貢獻了針對提交目錄,日誌聚合目錄可以負載均衡到各個NameSpace的設想,具體Issue見:YARN-9634

NameNode用戶的擁塞控制

在這裏插入圖片描述
社區提出了FairCallQueue ,如上圖所示,原有的FIFO的RPC結構,改成了Fair的結構,來對高頻率的單賬戶進行緩解和限制,詳細issue見:HADOOP-10282。應用以後,有效的隔離了Presto等即使查詢用戶併發量聚集時候,對HDFS其他線上業務的影響。

目前我們使用了FairCallQueue + RPC Backoff, 能滿足我們擁塞控制的需求。有效限制了異常高負載的用戶對整體RPC可用性的影響。

針對用戶較多的NameSpace我們正準備進行用戶優先級分更多層級,進行多層的Qos保障。

異步化各種操作提高NameNode的吞吐量

editlog和auditlog的異步化
原先版本的NameNode的editlog的行爲和auditlog的行爲都是同步阻塞的,這對NameNode的吞吐量影響很大,爲此我們把這editlog和auditlog兩個行爲改成了異步化。

塊彙報的優化

數據量越來越大以後,對NameNode的堆棧信息統計後發現,塊彙報的壓力對用戶的影響較大,爲此我們考慮對塊彙報進行了優化。首先全量塊彙報的時候加鹽,分散整體彙報對NameNode的壓力。然後增量彙報的時候進行如下優化:

首先把NameNode端的塊彙報異步進行聚合,有效的緩解了RPC的壓力,對應的Issue爲:HDFS-9198

然後相應的DataNode端的塊彙報也進行了批量聚合,對應Issue爲:HDFS-9710

NameNode鎖時間追蹤

HDFS-10872 添加了NameNode鎖住時間對應的Metrics。NameNode的鎖隊列長度堆積過高的時候,我們增加了全局鎖對應的鎖住時間,對某些鎖佔用時間過長的情況,進行分析,對很多鎖優化對細節很有幫助。

Decommission的改進

大集羣的Decommission操作非常常見,如機器遷移下線,機器故障需要下線等。而舊的Decommssion代碼存在如下的問題:

  1. 遍歷每個節點,對每個disk進行遍歷,負載集中,沒有分佈到各個節點和各個磁盤,會導致命中的那個磁盤非常熱。
  2. 通過上述NameNode的鎖時間追蹤發現,加入一個DataNode進行下線,會佔用較長時間的寫鎖。
  3. replica隊列堆積的問題。
  4. 等待複製隊列多次判斷是否replica,會重複佔用寫鎖的時間。
    社區最近也有了更完善的實現:HDFS-14854

Qos保障,業務控制,限流以及作業追蹤

軟限制和作業追蹤

針對訪問過高的用戶,進行審計增強,目前的審計日誌無法獲取用戶的作業信息。某些異常作業對某些治理不夠完善的大表的瘋狂訪問等等行爲,會對集羣造成很大的穩定性和性能影響。爲此我們引入了審計增強。
這一塊需要改的比較多包括計算引擎,以及YARN,HDFS都需要改一些依賴的Patch,主要有:
HDFS相關:HDFS-9184
YARN相關:YARN-4349
HIVE相關:HIVE-12254
SPARK相關:SPARK-15857
Flink 還沒有CallerContext內置,我們提了個Issue,待完善:FLINK-16809
Flink入庫對HDFS的壓力還是非常大的,加上業務濫用,有很嚴重的小文件問題見FLINK-11937
這樣審計日誌中就有了關鍵的作業信息,然後通過打到Kafka,Flink做實時分析,就能很容易的定位到HDFS高負載的作業。

硬限制:NameNode源碼改動

另外一種硬的方式,不做事後分析,做事前強限制:
上述提到過擁塞控制是用戶對應的擁塞控制,這裏對目錄進行硬限制,因爲除了不合理的用戶的高頻訪問,還存在大表或者治理非常不完善的目錄或者庫表,可以做QPS限制,可以針對如create,delete等操作,在NameNode代碼裏對相關的目錄對應的RPC做窗口的統計,如果QPS大於閾值則對客戶端返回一個重試信息,進行限流。

用戶體驗和運維便利性

自研 HDFS Proxy

由於歷史原因導致,很多算法等業務需要獨立的客戶端進行管理,而業務的激增導致了客戶端配置的頻繁更新造成了很大的人力運維成本。且客戶端的種類過於繁多,例如調度客戶機,容器化的調度客戶機,普通gateway等等。爲了實現配置轉移到了服務端進行控制,我們開發了HDFS Proxy,客戶端無需配置,Hdfs Client 將請求轉發到對應的HDFS Proxy Server。Proxy 可以橫向擴展,上面掛了一層負載均衡器。非常輕量級,已經使用了將近半年,由於viewFs客戶端維護方式很不利於運維管理,且我們當前版本比較老,且Router不夠成熟,主要用於和Router進行過渡。
具體結構如下圖所示:
在這裏插入圖片描述

HDFS Router 改進和二次開發

隨着Router的成熟,和我們對Router進行了一些定製化的改進,我們慢慢從我們輕量級的HDFS Proxy切換到Router,畢竟開源的力量是偉大的,我們也要站在巨人的肩膀上。

Router審計日誌的完善和作業追蹤

Router本身對AuditLog支持對不好,爲此我們增加了定製對AuitLog,並且準備繼續在Router這一層對任務進行追蹤。在Router層實現軟限制和作業追蹤。

Router Trash重構和RPC優化

針對數據成本優化,我們做了Hive生命週期的項目,每天都有大量的Trash操作。
Router對Trash的支持很不好,社區有類似客戶端的修改方案,但是很不友好,爲此我們對Trash操作進行了重構,增加的新的RPC調用。
重構後不僅解決了Router中對應多個NameSpace的刪除操作。還把之前的Trash對NameNode的RPC負載降低了50%,客戶端從(mkdir rename) -> trash。
這一塊也貢獻給了社區,待完善:HDFS-15083

Router 支持全局Quota管控

如果單個目錄掛載了多個NameSpace,Router目前也支持了全局的Quota管控,但還有部分細節需要完善。

Router rename across Federation

針對Federation的各個NameSpace之間的FastCopy上述有做了介紹,Router有個功能可以實現類似FastMove的功能,針對跨NameSpace的已掛載的目錄,可以進行rename到其他NameSpace。

HDFS目錄實時解析

HDFS的目錄信息解析,需要從FSImage進行解析,集羣大了以後,我們FSImage達到幾十上百G,解析過程相當緩慢,只能以 T+1的方式進行解析。
爲此,我們開發了準實時的解析項目,來應對,例如:短時間內存儲增量巨大的目錄,小文件數量劇增的目錄。
聚合策略:利用EditLog的操作碼,通過實時流,利用Tidb或者Durid等進行準實時聚合。
操作數種類比較多,主要追蹤的操作有:OP_DELETE, OP_MKDIR, OP_ADD, OP_UPDATE_BLOCKS, OP_CLOSE, OP_RENAME_OLD。

服務的穩定性和性能

由於Federation架構8組業務的NameSpace共享同一個DataNode底層服務,加上本身我們的機型磁盤塊很多,且業務的複雜性多樣性對DataNode的訪問,NameNode的壓力轉移到了DataNode上面。

DataNode DU導致IO重和重啓Uncached問題

存儲從DU改爲內存計算

DataNode默認使用DU對存儲總量進行彙總給 NameNode,DU操作對DataNode的IO壓力比較大,且DataNode的IO沒有和全局鎖進行分離,IO也會佔用鎖的時間。DU對IO壓力大的解決方案有多種,分散DU的時間加個隨機數然後分佈到各個節點,減緩整體的IO,但是無法避免還是需要DU操作。

針對磁盤的DU,我們把存儲總量的計算放到了內存裏,因爲內存裏本身有磁盤塊的信息,通過內存數據結果進行定期計算。具體最新的Issue:HDFS-9710

解決重啓Uncached問題

DataNode滾動的時候,經常會有ungraceful shutdown的情況,會導致存儲量的緩存沒有緩存到本地,那麼啓動的時候就會重複去計算,針對DU的場景會導致重啓時間變得很長,爲此我們加了定時線程對緩存進行更新,重啓的時候就不用去重新計算存儲總量了。具體Issue見:HDFS-15171

慢節點和讀寫長尾優化

當集羣節點日益增長當時候,很容易產生DataNode節點老化導致磁盤或者網絡IO慢等其他問題,這就會造成用戶的讀寫長尾等問題。
DataNode端的metrics收集: HDFS-10917慢節點監控,然後心跳彙報給NameNode:HDFS-11194

除了DataNode的慢節點監控,以及NameNode彙總慢節點信息,也能從客戶端去監控讀寫速度以及讀寫長尾的DataNode節點,這一塊社區也有對應的實現,有待完善:HDFS-12861

開啓客戶端併發讀:
針對慢讀取另起一個線程併發讀,線程池的大小
dfs.client.hedged.read.threadpool.size
慢讀取的閾值
dfs.client.hedged.read.threshold.millis (默認是500)

寫慢節點:
寫慢節點的時候,配合慢節點的情況,做快速的PipeLine Recovery。

DataNode鎖優化之旅

業務量激增導致8組NameNode的負載都打到了DataNode,突然出現DataNode心跳時間陡增到數分鐘,導致心跳沒有即時收到,DataNode經常在高峯期批量Dead,對業務造成了很嚴重的影響。
爲此我們分析的DataNode的堆棧情況,發現是由於DataNode心跳的全局鎖被其他併發過高的讀寫等操作佔用,導致關鍵心跳線程被Blocked住。

Synchronized非公平鎖改爲ReentrantLock公平鎖

首先,我們把DataNode的Synchronized非公平鎖改成了,ReentrantLock默認爲公平鎖。
在這裏插入圖片描述

ReentrantLock公平鎖拆成公平的讀寫鎖

針對全局的可重入ReentrantLock,拆成了讀寫鎖,效果很好,堵住幾千個線程緩解了很多。
在這裏插入圖片描述
在這裏插入圖片描述

拆分成以BlockPool爲單位的細粒度讀寫鎖

繼續拆鎖,拆分成以BlockPool爲單位的讀寫鎖,意味着,如果你有8組NameSpace的話,一個DataNode全局鎖,可以拆分成8把鎖。
我在社區提了個Issue:HDFS-15180
從灰度的節點看,鎖的進一步拆分,帶來了預期的效果,沒有拆分之前,Directory Scan掃描操作會佔用較長的鎖時間,經常長達10幾秒,甚至幾十秒:
在這裏插入圖片描述
滾動以後,因爲每個BlockPool單獨掃描,鎖住時間降低到2s, 1s,甚至更小。
在這裏插入圖片描述
HDFS的存儲量計算,從DU改爲內存計算以後,內存中的deepcopy部分本身會佔用較長的鎖時間超過 300ms,拆分爲BlockPool鎖以後,沒有超過300ms的情況出現。
拆分前有超過300ms的情況較多:
在這裏插入圖片描述

最終目標,最小單位的鎖

最終目標,一個Volume中對應的BlockPool單位拆成一把鎖。如下圖所示,如果HDFS Federation有4組NameSpace,每個DataNode有3個磁盤塊。那麼就對應了4個BlockPool(BP),和3個Volume,原生的DataNode全局鎖是一把鎖,理想的情況下,在這個例子情況下是可以拆分成爲4*3 = 12 把讀寫鎖,對應鎖住的範圍就是兩個橢圓的交接重合部分。
目前我們已經拆成了BlockPool爲單位,對應到這個例子就是4把讀寫鎖,對性能的提升效果不錯。
在這裏插入圖片描述

DataNode 接受指令異步化

我們查看日誌的時候發現,DataNode接受指令的時候,會把心跳線程給阻塞住,爲此把阻塞的線程改成異步的線程池去處理這個指令操作,這樣不會把心跳線程給堵住。
如下圖所示,修改之前,心跳阻塞時間可以達到幾十秒。
在這裏插入圖片描述

DataNode IO和鎖分離

DataNode的IO操作,有時候會佔用很長的鎖時間,爲此我們正準備把IO和鎖進行分離。

進行中的和未來規劃

  • 我們在測試環境已經測試了Hadoop3的新功能,準備在新集羣遷移的時候使用,完成Hadoop2到Hadoop3的升級 。
  • Router還有很多功能正在不斷完善。
  • 爲了解決NameNode讀佔比大的問題,我們準備對Hadoop3嘗試讀寫分離功能,把讀轉移到Standby NameNode。
  • 持續跟進新功能:
    1. NameNode分段鎖:HDFS-14703 , 解決NameNode鎖吞吐量的問題。
    2. Ozone,解決小文件,和NameNode擴展性的問題,數據存儲底層也是用的DataNode。看了部分Ozone在開發中的代碼,有個存儲原地改變的功能還在開發中,DataNode的數據直接從HDFS轉成Ozone,很不錯的功能,可能還有其他驚喜。
    3. EC的冷存儲和公有云OSS的冷存儲,成本和性能進行對比。
    4. HDFS操作全面異步化: HDFS-9924
    5. NameNode啓動時間優化。

歡迎業內交流:
微信: zhu821684824
Apache Jira: https://issues.apache.org/jira/secure/ViewProfile.jspa?name=zhuqi
郵箱:
[email protected]
[email protected]

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