HDFS NameNode重啟優化

ReinaldoBoe 7年前發布 | 9K 次閱讀 HDFS 軟件架構

一、背景

在Hadoop集群整個生命周期里,由于調整參數、Patch、升級等多種場景需要頻繁操作NameNode重啟,不論采用何種架構,重啟期間集群整體存在可用性和可靠性的風險,所以優化NameNode重啟非常關鍵。

本文基于 Hadoop-2.x 和 HA with QJM 社區架構和系統設計(如圖1所示),通過梳理NameNode重啟流程,并在此基礎上,闡述對NameNode重啟優化實踐。

圖1 HDFS HA with QJM架構圖示

二、NameNode重啟流程

在HDFS的整個運行期里,所有元數據均在NameNode的內存集中管理,但是由于內存易失特性,一旦出現進程退出、宕機等異常情況,所有元數據都會丟失,給整個系統的數據安全會造成不可恢復的災難。為了更好的容錯能力,NameNode會周期進行Checkpoint,將其中的一部分元數據(文件系統的目錄樹Namespace)刷到持久化設備上,即二進制文件FSImage,這樣的話即使NameNode出現異常也能從持久化設備上恢復元數據,保證了數據的安全可靠。

但是僅周期進行Checkpoint仍然無法保證所有數據的可靠,如前次Checkpoint之后寫入的數據依然存在丟失的問題,所以將兩次Checkpoint之間對Namespace寫操作實時寫入EditLog文件,通過這種方式可以保證HDFS元數據的絕對安全可靠。

事實上,除Namespace外,NameNode還管理非常重要的元數據BlocksMap,描述數據塊Block與DataNode節點之間的對應關系。NameNode并沒有對這部分元數據同樣操作持久化,原因是每個DataNode已經持有屬于自己管理的Block集合,將所有DataNode的Block集合匯總后即可構造出完整BlocksMap。

HA with QJM架構下,NameNode的整個重啟過程中始終以SBN(StandbyNameNode)角色完成。與前述流程對應,啟動過程分以下幾個階段:

  1. 加載FSImage;
  2. 回放EditLog;
  3. 執行Checkpoint;(非必須步驟,結合實際情況和參數確定,后續詳述)
  4. 收集所有DataNode的注冊和數據塊匯報;

默認情況下,NameNode會保存兩個FSImage文件,與此對應,也會保存對應兩次Checkpoint之后的所有EditLog文件。一般來說,NameNode重啟后,通過對FSImage文件名稱判斷,選擇加載最新的FSImage文件及回放該Checkpoint之后生成的所有EditLog,完成后根據加載的EditLog中操作條目數及距上次Checkpoint時間間隔(后續詳述)確定是否需要執行Checkpoint,之后進入等待所有DataNode注冊和元數據匯報階段,當這部分數據收集完成后,NameNode的重啟流程結束。

從線上NameNode歷次重啟時間數據看,各階段耗時占比基本接近如圖2所示。

圖2 NameNode重啟各階段耗時占比

經過優化,在元數據總量540M(目錄樹240M,數據塊300M),超過4K規模的集群上重啟NameNode總時間~35min,其中加載FSImage耗時~15min,秒級回放EditLog,數據塊匯報耗時~20min,基本能夠滿足生產環境的需求。

2.1 加載FSImage

如前述,FSImage文件記錄了HDFS整個目錄樹Namespace相關的元數據。從 Hadoop-2.4.0 起,FSImage開始采用 Google Protobuf 編碼格式描述( HDFS-5698 ),詳細描述文件見 fsimage.proto 。根據描述文件和實現邏輯,FSImage文件格式如圖3所示。

圖3 FSImage文件格式

從fsimage.proto和FSImage文件存儲格式容易看到,除了必要的文件頭部校驗(MAGIC)和尾部文件索引(FILESUMMARY)外,主要包含以下核心數據:

(0)NS_INFO(NameSystemSection):記錄HDFS文件系統的全局信息,包括NameSystem的ID,當前已經分配出去的最大BlockID以及TransactionId等信息;

(1)INODE(INodeSection):整個目錄樹所有節點數據,包括INodeFile/INodeDirectory/INodeSymlink等所有類型節點的屬性數據,其中記錄了如節點id,節點名稱,訪問權限,創建和訪問時間等等信息;

(2)INODE_DIR(INodeDirectorySection):整個目錄樹中所有節點之間的父子關系,配合INODE可構建完整的目錄樹;

(3)FILES_UNDERCONSTRUCTION(FilesUnderConstructionSection):尚未完成寫入的文件集合,主要為重啟時重建Lease集合;

(4)SNAPSHOT(SnapshotSection):記錄Snapshot數據,快照是Hadoop 2.1.0引入的新特性,用于數據備份、回滾,以防止因用戶誤操作導致集群出現數據問題;

(5)SNAPSHOT_DIFF(SnapshotDiffSection):執行快照操作的目錄/文件的Diff集合數據,與SNAPSHOT一起構建較完整的快照管理能力;

(6)INODE_REFERENCE(INodeReferenceSection):當目錄/文件被操作處于快照,且該目錄/文件被重命名后,會存在多條訪問路徑,INodeReference就是為了解決該問題;

(7)SECRET_MANAGER(SecretManagerSection):記錄DelegationKey和DelegationToken數據,根據DelegationKey及由DelegationToken構造出的DelegationTokenIdentifier方便進一步計算密碼,以上數據可以完善所有合法Token集合;

(8)CACHE_MANAGER(CacheManagerSection):集中式緩存特性全局信息,集中式緩存特性是Hadoop-2.3.0為提升數據讀性能引入的新特性;

(9)STRING_TABLE(StringTableSection):字符串到id的映射表,維護目錄/文件的Permission字符到ID的映射,節省存儲空間;

NameNode執行Checkpoint時,遵循Protobuf定義及上述文件格式描述,重啟加載FSImage時,同樣按照Protobuf定義的格式從文件流中讀出相應數據構建整個目錄樹Namespace及其他元數據。將FSImage文件從持久化設備加載到內存并構建出目錄樹結構后,實際上并沒有完全恢復元數據到最新狀態,因為每次Checkpoint之后還可能存在大量HDFS寫操作。

2.2 回放EditLog

NameNode在響應客戶端的寫請求前,會首先更新內存相關元數據,然后再把這些操作記錄在EditLog文件中,可以看到內存狀態實際上要比EditLog數據更及時。

記錄在EditLog之中的每個操作又稱為一個事務,對應一個整數形式的事務編號。在當前實現中多個事務組成一個Segment,生成獨立的EditLog文件,其中文件名稱標記了起止的事務編號,正在寫入的EditLog文件僅標記起始事務編號。EditLog文件的格式非常簡單,沒再通過Google Protobuf描述,文件格式如圖4所示。

圖4 EditLog文件格式

一個完整的EditLog文件包括四個部分內容,分別是:

(0)LAYOUTVERSION:版本信息;

(1)OP_START_LOG_SEGMENT:標識文件開始;

(2)RECORD:順序逐個記錄HDFS寫操作的事務內容;

(3)OP_END_LOG_SEGMENT:標記文件結束;

NameNode加載FSImage完成后,即開始對該FSImage文件之后(通過比較FSImage文件名稱中包含的事務編號與EditLog文件名稱的起始事務編號大小確定)生成的所有EditLog嚴格按照事務編號從小到大逐個遵循上述的格式進行每一個HDFS寫操作事務回放。

NameNode加載完所有必需的EditLog文件數據后,內存中的目錄樹即恢復到了最新狀態。

2.3 DataNode注冊匯報

經過前面兩個步驟,主要的元數據被構建,HDFS的整個目錄樹被完整建立,但是并沒有掌握從數據塊Block與DataNode之間的對應關系BlocksMap,甚至對DataNode的情況都不掌握,所以需要等待DataNode注冊,并完成對從DataNode匯報上來的數據塊匯總。待匯總的數據量達到預設比例(dfs.namenode.safemode.threshold-pct)后退出Safemode。

NameNode重啟經過加載FSImage和回放EditLog后,所有DataNode不管進程是否發生過重啟,都必須經過以下兩個步驟:

(0)DataNode重新注冊RegisterDataNode;

(1)DataNode匯報所有數據塊BlockReport;

對于節點規模較大和元數據量較大的集群,這個階段的耗時會非常可觀。主要有三點原因:

(0)處理BlockReport的邏輯比較復雜,相對其他RPC操作耗時較長。圖5對比了BlockReport和AddBlock兩種不同RPC的處理時間,盡管AddBlock操作也相對復雜,但是對比來看,BlockReport的處理時間顯著高于AddBlock處理時間;

(1)NameNode對每一個BlockReport的RPC請求處理都需要持有全局鎖,也就是說對于BlockReport類型RPC請求實際上是串行處理;

(2)NameNode重啟時所有DataNode集中在同一時間段進行BlockReport請求;

(點擊放大圖像)

圖5 BlockReport和AddBlock兩個RPC處理時間對比

前文 NameNode內存全景 中詳細描述過Block在NameNode元數據中的關鍵作用及與Namespace/DataNode/BlocksMap的復雜關系,從中也可以看出,每個新增Block需要維護多個關系,更何況重啟過程中所有Block都需要建立同樣復雜關系,所以耗時相對較高。

三、重啟優化

根據前面對NameNode重啟過程的簡單梳理,在各個階段可以適當的實施優化以加快NameNode重啟過程。

0、 HDFS-7097 解決重啟過程中SBN執行Checkpoint時不能處理BlockReport請求的問題;

Fix: 2.7.0

Hadoop-2.7.0版本前,SBN(StandbyNameNode)在執行Checkpoint操作前會先獲得全局讀寫鎖fsLock,在此期間,BlockReport請求由于不能獲得全局寫鎖會持續處于等待狀態,直到Checkpoint完成后釋放了fsLock鎖后才能繼續。NameNode重啟的第三個階段,同樣存在這種情況。而且對于規模較大的集群,每次Checkpoint時間在分鐘級別,對整個重啟過程影響非常大。實際上,Checkpoint是對目錄樹的持久化操作,并不涉及BlocksMap數據結構,所以Checkpoint期間是可以讓BlockReport請求直接通過,這樣可以節省期間BlockReport排隊等待帶來的時間開銷, HDFS-7097 正是將鎖粒度放小解決了Checkpoint過程不能處理BlockReport類型RPC請求的問題。

HDFS-7097 相對,另一種思路也值得借鑒,就是重啟過程盡可能避免出現Checkpoint。觸發Checkpoint有兩種情況:時間周期或HDFS寫操作事務數,分別通過參數dfs.namenode.checkpoint.period和dfs.namenode.checkpoint.txns控制,默認值分別是3600s和1,000,000,即默認情況下一個小時或者寫操作的事務數超過1,000,000觸發一次Checkpoint。為了避免在重啟過程中頻繁執行Checkpoint,可以適當調大dfs.namenode.checkpoint.txns,建議值10,000,000 ~ 20,000,000,帶來的影響是EditLog文件累計的個數會稍有增加。從實踐經驗上看,對一個有億級別元數據量的NameNode,回放一個EditLog文件(默認1,000,000寫操作事務)時間在秒級,但是執行一次Checkpoint時間通常在分鐘級別,綜合權衡減少Checkpoint次數和增加EditLog文件數收益比較明顯。

1、 HDFS-6763 解決SBN每間隔1min全局計算和驗證Quota值導致進程Hang住數秒的問題;

Fix: 2.8.0

ANN(ActiveNameNode)將HDFS寫操作實時寫入JN的EditLog文件,為同步數據,SBN默認間隔1min從JN拉取一次EditLog文件并進行回放,完成后執行全局Quota檢查和計算,當Namespace規模變大后,全局計算和檢查Quota會非常耗時,在此期間,整個SBN的Namenode進程會被Hang住,以至于包括DN心跳和BlockReport在內的所有RPC請求都不能及時處理。NameNode重啟過程中這個問題影響突出。

實際上,SBN在EditLog Tailer階段計算和檢查Quota完全沒有必要,HDFS-6763將這段處理邏輯后移到主從切換時進行,解決SBN進程間隔1min被Hang住的問題。

從優化效果上看,對一個擁有接近五億元數據量,其中兩億數據塊的NameNode,優化前數據塊匯報階段耗時~30min,其中觸發超過20次由于計算和檢查Quota導致進程Hang住~20s的情況,整個BlockReport階段存在超過5min無效時間開銷,優化后可到~25min。

2、 HDFS-7980 簡化首次BlockReport處理邏輯優化重啟時間;

Fix: 2.7.1

NameNode加載完元數據后,所有DataNode嘗試開始進行數據塊匯報,如果匯報的數據塊相關元數據還沒有加載,先暫存消息隊列,當NameNode完成加載相關元數據后,再處理該消息隊列。對第一次塊匯報的處理比較特別(NameNode重啟后,所有DataNode的BlockReport都會被標記成首次數據塊匯報),為提高處理速度,僅驗證塊是否損壞,之后判斷塊狀態是否為FINALIZED,若是建立數據塊與DataNode的映射關系,建立與目錄樹中文件的關聯關系,其他信息一概暫不處理。對于非初次數據塊匯報,處理邏輯要復雜很多,對報告的每個數據塊,不僅檢查是否損壞,是否為FINALIZED狀態,還會檢查是否無效,是否需要刪除,是否為UC狀態等等;驗證通過后建立數據塊與DataNode的映射關系,建立與目錄樹中文件的關聯關系。

初次數據塊匯報的處理邏輯獨立出來,主要原因有兩方面:

(0)加快NameNode的啟動時間;測試數據顯示含~500M元數據的NameNode在處理800K個數據塊的初次塊匯報的處理時間比正常塊匯報的處理時間可降低一個數量級;

(1)啟動過程中,不提供正常讀寫服務,所以只要確保正常數據(整個Namespace和所有FINALIZED狀態Blocks)無誤,無效和冗余數據處理完全可以延后到IBR(IncrementalBlockReport)或下次BR(BlockReport);

這本來是非常合理和正常的設計邏輯,但是實現時NameNode在判斷是否為首次數據塊塊匯報的邏輯一直存在問題,導致這段非常好的改進點邏輯實際上長期并未真正執行到,直到 HDFS-7980 在Hadoop-2.7.1修復該問題。 HDFS-7980 的優化效果非常明顯,測試顯示,對含80K Blocks的BlockReport RPC請求的處理時間從~500ms可優化到~100ms,從重啟期整個BlockReport階段看,在超過600M元數據,其中300M數據塊的NameNode顯示該階段從~50min優化到~25min。

3、 HDFS-7503 解決重啟前大刪除操作會造成重啟后鎖內寫日志降低處理能力;

Fix: 2.7.0

若NameNode重啟前產生過大刪除操作,當NameNode加載完FSImage并回放了所有EditLog構建起最新目錄樹結構后,在處理DataNode的BlockReport時,會發現有大量Block不屬于任何文件,Hadoop-2.7.0版本前,對于這類情況的輸出日志邏輯在全局鎖內,由于存在大量IO操作的耗時,會嚴重拉長處理BlockReport的處理時間,影響NameNode重啟時間。 HDFS-7503 的解決辦法非常簡單,把日志輸出邏輯移出全局鎖外。線上效果上看對同類場景優化比較明顯,不過如果重啟前不觸發大的刪除操作影響不大。

4、防止熱備節點SBN(StandbyNameNode)/冷備節點SNN(SecondaryNameNode)長時間未正常運行堆積大量Editlog拖慢NameNode重啟時間;

不論選擇HA熱備方案SBN(StandbyNameNode)還是冷備方案SNN(SecondaryNameNode)架構,執行Checkpoint的邏輯幾乎一致,如圖6所示。如果SBN/SNN服務長時間未正常運行,Checkpoint不能按照預期執行,這樣會積壓大量EditLog。積壓的EditLog文件越多,重啟NameNode需要加載EditLog時間越長。所以盡可能避免出現SNN/SBN長時間未正常服務的狀態。

圖6 Checkpoint流程

在一個有500M元數據的NameNode上測試加載一個200K次HDFS事務操作的EditLog文件耗時~5s,按照默認2min的EditLog滾動周期,如果一周時間SBN/SNN未能正常工作,則會累積~5K個EditLog文件,此后一旦發生NameNode重啟,僅加載EditLog文件的時間就需要~7h,也就是整個集群存在超過7h不可用風險,所以切記要保證SBN/SNN不能長時間故障。

5、 HDFS-6425 HDFS-6772 NameNode重啟后DataNode快速退出blockContentsStale狀態防止PostponedMisreplicatedBlocks過大影響對其他RPC請求的處理能力;

Fix: 2.6.0 , 2.7.0

當集群中大量數據塊的實際存儲副本個數超過副本數時(跨機房架構下這種情況比較常見),NameNode重啟后會迅速填充到PostponedMisreplicatedBlocks,直到相關數據塊所在的所有DataNode匯報完成且退出Stale狀態后才能被清理。如果PostponedMisreplicatedBlocks數據量較大,每次全遍歷需要消耗大量時間,且整個過程也要持有全局鎖,嚴重影響處理BlockReport的性能, HDFS-6425HDFS-6772 分別將可能在BlockReport邏輯內部遍歷非常大的數據結構PostponedMisreplicatedBlocks優化到異步執行,并在NameNode重啟后讓DataNode快速退出blockContentsStale狀態避免PostponedMisreplicatedBlocks過大入手優化重啟效率。

6、降低BlockReport時數據規模;

NameNode處理BlockReport的效率低主要原因還是每次BlockReport所帶的Block規模過大造成,所以可以通過調整Block數量閾值,將一次BlockReport分成多盤分別匯報,以提高NameNode對BlockReport的處理效率。可參考的參數為:dfs.blockreport.split.threshold,默認值1,000,000,即當DataNode本地的Block個數超過1,000,000時才會分盤進行匯報,建議將該參數適當調小,具體數值可結合NameNode的處理BlockReport時間及集群中所有DataNode管理的Block量分布確定。

7、重啟完成后對比檢查數據塊上報情況;

前面提到NameNode匯總DataNode上報的數據塊量達到預設比例(dfs.namenode.safemode.threshold-pct)后就會退出Safemode,一般情況下,當NameNode退出Safemode后,我們認為已經具備提供正常服務的條件。但是對規模較大的集群,按照這種默認策略及時執行主從切換后,容易出現短時間丟塊的問題。考慮在200M數據塊的集群,默認配置項dfs.namenode.safemode.threshold-pct=0.999,也就是當NameNode收集到200M*0.999=199.8M數據塊后即可退出Safemode,此時實際上還有200K數據塊沒有上報,如果強行執行主從切換,會出現大量的丟塊問題,直到數據塊匯報完成。應對的辦法比較簡單,嘗試調大dfs.namenode.safemode.threshold-pct到1,這樣只有所有數據塊上報后才會退出Safemode。但是這種辦法一樣不能保證萬無一失,如果啟動過程中有DataNode匯報完數據塊后進程掛掉,同樣存在短時間丟失數據的問題,因為NameNode匯總上報數據塊時并不檢查副本數,所以更穩妥的解決辦法是利用主從NameNode的JMX數據對比所有DataNode當前匯報數據塊量的差異,當差異都較小后再執行主從切換可以保證不發生上述問題。

8、其他;

除了優化NameNode重啟時間,實際運維中還會遇到需要滾動重啟集群所有節點或者一次性重啟整集群的情況,不恰當的重啟方式也會嚴重影響服務的恢復時間,所以合理控制重啟的節奏或選擇合適的重啟方式尤為關鍵, HDFS集群啟動方式分析 一文對集群重啟方式進行了詳細的闡述,這里就不再展開。

經過多次優化調整,從線上NameNode歷次的重啟時間監控指標上看,收益非常明顯,圖7截取了其中幾次NameNode重啟時元數據量及重啟時間開銷對比,圖中直觀顯示在500M元數據量級下,重啟時間從~4000s優化到~2000s。

圖7 NameNode重啟時間對比

這里羅列了一小部分實踐過程中可以有效優化重啟NameNode時間或者重啟全集群的點,其中包括了社區成熟Patch和相關參數優化,雖然實現邏輯都很小,但是實踐收益非常明顯。當然除了上述提到,NameNode重啟還有很多可以優化的地方,比如優化FSImage格式,并行加載等等,社區也在持續關注和優化,部分討論的思路也值得關注、借鑒和參考。

四、總結

NameNode重啟甚至全集群重啟在整個Hadoop集群的生命周期內是比較頻繁的運維操作,優化重啟時間可以極大提升運維效率,避免可能存在的風險。本文通過分析NameNode啟動流程,并結合實踐過程簡單羅列了幾個供參考的有效優化點,借此希望能給實踐過程提供可優化的方向和思路。

五、參考

NameNode內存全景

NameNode內存詳解

Apache Hadoop

Hadoop Source

HDFS Issues

Cloudera Blog

 

 

來自:http://www.infoq.com/cn/articles/namenode-restart-optimization

 

 本文由用戶 ReinaldoBoe 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!