HDFS 異常處理與恢復

在前面的文章 《HDFS DataNode 設計實現解析》中咱們對文件操做進行了描述,但並未展開講述其中涉及的異常錯誤處理與恢復機制。本文將深刻探討 HDFS 文件操做涉及的錯誤處理與恢復過程。html

讀異常與恢復

讀文件可能發生的異常有兩種:算法

  1. 讀取過程當中 DataNode 掛了
  2. 讀取到的文件數據損壞

HDFS 的文件塊多副本分散存儲機制保障了數據存儲的可靠性,對於第一種狀況 DataNode 掛了只須要失敗轉移到其餘副本所在的 DataNode 繼續讀取,而對於第二種狀況讀取到的文件數據塊若校驗失敗可認定爲損壞,依然能夠轉移到讀取其餘無缺的副本,並向 NameNode 彙報該文件 block 損壞,後續處理由 NameNode 通知 DataNode 刪除損壞文件 block,並根據無缺的副原本複製一份新的文件 block 副本。apache

由於讀文件不涉及數據的改變,因此處理起來相對簡單,恢復機制的透明性和易用性都很是好。微信

寫異常與恢復

以前的文章中對寫文件的過程作了描述,這個過程當中可能發生多種不一樣的錯誤異常對應着不一樣的處理方式。先看看有哪些可能的異常?markdown

異常模式

可能的異常模式以下所列:網絡

  • Client 在寫入過程當中,本身掛了
  • Client 在寫入過程當中,有 DataNode 掛了
  • Client 在寫入過程當中,NameNode 掛了

對於以上所列的異常模式,都有分別對應的恢復模式。app

恢復模式

當 Client 在寫入過程當中,本身掛了。因爲 Client 在寫文件以前須要向 NameNode 申請該文件的租約(lease),只有持有租約才容許寫入,並且租約須要按期續約。因此當 Client 掛了後租約會超時,HDFS 在超時後會釋放該文件的租約並關閉該文件,避免文件一直被這個掛掉的 Client 獨佔致使其餘人不能寫入。這個過程稱爲 lease recovery。分佈式

在發起 lease recovery 時,若多個文件 block 副本在多個 DataNodes 上處於不一致的狀態,首先須要將其恢復到一致長度的狀態。這個過程稱爲 block recovery。 這個過程只能在 lease recovery 過程當中發起。ide

當 Client 在寫入過程當中,有 DataNode 掛了。寫入過程不會馬上終止(若是馬上終止,易用性和可用性都太不友好),取而代之 HDFS 嘗試從流水線中摘除掛了的 DataNode 並恢復寫入,這個過程稱爲 pipeline recovery。oop

當 Client 在寫入過程當中,NameNode 掛了。這裏的前提是已經開始寫入了,因此 NameNode 已經完成了對 DataNode 的分配,若一開始 NameNode 就掛了,整個 HDFS 是不可用的因此也沒法開始寫入。流水線寫入過程當中,當一個 block 寫完後需向 NameNode 報告其狀態,這時 NameNode 掛了,狀態報告失敗,但不影響 DataNode 的流線工做,數據先被保存下來,但最後一步 Client 寫完向 NameNode 請求關閉文件時會出錯,因爲 NameNode 的單點特性,因此沒法自動恢復,需人工介入恢復。

上面先簡單介紹了對應異常的恢復模式,詳細過程後文再描述。在介紹詳細恢復過程前,須要瞭解文件數據狀態的概念。由於寫文件過程當中異常和恢復會對數據狀態產生影響,咱們知道 HDFS 文件至少由 1 個或多個 block 構成,所以每一個 block 都有其相應的狀態,因爲文件的元數據在 NameNode 中管理而文件數據自己在 DataNode 中管理,爲了區分文件 block 分別在 NameNode 和 DataNode 上下文語境中的區別,下面咱們會用 replica(副本)特指在 DataNode 中的 block,而 block 則限定爲在 NameNode 中的文件塊元數據信息。在這個語義限定下 NameNode 中的 block 實際對應 DataNodes 上的多個 replicas,它們分別有不一樣的數據狀態。咱們先看看 replica 和 block 分別在 DataNode 和 NameNode 中都存在哪些狀態?

Replica 狀態

Replica 在 DataNode 中存在的狀態列表以下:

  • FINALIZED:代表 replica 的寫入已經完成,長度已肯定,除非該 replica 被從新打開並追加寫入。
  • RBW:該狀態是 Replica Being Written 的縮寫,代表該 replica 正在被寫入,正在被寫入的 replica 老是打開文件的最後一個塊。
  • RWR:該狀態是 Replica Waiting to be Recovered 的縮寫,假如寫入過程當中 DataNode 掛了重啓後,其上處於 RBW 狀態的 replica 將被變動爲 RWR 狀態,這個狀態說明其數據須要恢復,由於在 DataNode 掛掉期間其上的數據可能過期了。
  • RUR:該狀態是 Replica Under Recovery 的縮寫,代表該 replica 正處於恢復過程當中。
  • TEMPORARY:一個臨時狀態的 replica 是由於複製或者集羣平衡的須要而建立的,若複製失敗或其所在的 DataNode 發生重啓,全部臨時狀態的 replica 會被刪除。臨時態的 replica 對外部 Client 來講是不可見的。

DataNode 會持久化存儲 replica 的狀態,每一個數據目錄都包含了三個子目錄:

  • current:目錄包含了 FINALIZED 狀態 replicas。
  • tmp:目錄包含了 TEMPORARY 狀態的 replicas。
  • rbw:目錄則包含了 RBWRWRRUR 三種狀態的 relicas,從該目錄下加載的 replicas 默認都處於 RWR 狀態。

從目錄看出實際上只持久化了三種狀態,而在內存中則有五種狀態,從下面的 replica 狀態變遷圖也能夠看出這點。
這裏寫圖片描述

咱們從 Init 開始簡單描述下 replica 的狀態變遷圖。

  • Init 出發,一個新建立的 replica 初始化爲兩種狀態:
    • 由 Client 請求新建的 replica 用於寫入,狀態爲 RBW
    • 由 NameNode 請求新建的 replica 用於複製或集羣間再平衡拷貝,狀態爲 TEMPORARY
  • RBW 出發,有三種狀況:
    • Client 寫完並關閉文件後,切換到 FINALIZED 狀態。
    • replica 所在的 DataNode 發生重啓,切換到 RWR 狀態,重啓期間數據可能過期了,能夠被丟棄。
    • replica 參與 block recovery 過程(詳見後文),切換到 RUR 狀態。
  • TEMPORARY 出發,有兩種狀況:
    • 複製或集羣間再平衡拷貝成功後,切換到 FINALIZED 狀態。
    • 複製或集羣間再平衡拷貝失敗或者所在 DataNode 發生重啓,該狀態下的 replica 將被刪除
  • RWR 出發,有兩種狀況:
    • 所在 DataNode 掛了,就變回了 RBW 狀態,由於持久化目錄 rbw 包含了三種狀態,重啓後又回到 RWR 狀態。
    • replica 參與 block recovery 過程(詳見後文),切換到 RUR 狀態。
  • RUR 出發,有兩種狀況:
    • 如上,所在 DataNode 掛了,就變回了 RBW 狀態,重啓後只會回到 RWR 狀態,看是否還有必要參與恢復仍是過期直接被丟棄。
    • 恢復完成,切換到 FINALIZED 狀態。
  • FINALIZED 出發,有兩種狀況:
    • 文件從新被打開追加寫入,文件的最後一個 block 對應的全部 replicas 切換到 RBW
    • replica 參與 block recovery 過程(詳見後文),切換到 RUR 狀態。

接下咱們再看看 NameNode 上 block 的狀態有哪些以及時如何變化的。

Block 狀態

Block 在 NameNode 中存在的狀態列表以下:

  • UNDER_CONSTRUCTION:當新建立一個 block 或一箇舊的 block 被從新打開追加時處於該狀態,處於改狀態的老是一個打開文件的最後一個 block。
  • UNDER_RECOVERY:當文件租約超時,一個處於 UNDER_CONSTRUCTION 狀態下 block 在 block recovery 過程開始後會變動爲該狀態。
  • COMMITTED:代表 block 數據已經不會發生變化,但向 NameNode 報告處於 FINALIZED 狀態的 replica 數量少於最小副本數要求。
  • COMPLETE:當 NameNode 收處處於 FINALIZED 狀態的 replica 數量達到最小副本數要求後,則切換到該狀態。只有當文件的全部 block 處於該狀態纔可被關閉。

NameNode 不會持久化存儲這些狀態,一旦 NameNode 發生重啓,它將全部打開文件的最後一個 block 設置爲 UNDER_CONSTRUCTION 狀態,其餘則所有設置爲 COMPLETE 狀態。

下圖展現了 block 的狀態變化過程。
這裏寫圖片描述

咱們仍是從 Init 開始簡單描述下 block 的狀態變遷圖。

  • Init 出發,只有當 Client 新建或追加文件寫入時新建立的 block 處於 UNDER_CONSTRUCTION 狀態。
  • UNDER_CONSTRUCTION 出發,有三種狀況:
    • 當客戶端發起 add block 或 close 請求,若處於 FINALIZED 狀態的 replica 數量少於最小副本數要求,則切換到 COMMITTED 狀態,
      這裏 add block 操做影響的是文件的倒數第二個 block 的狀態,而 close 影響文件最後一個 block 的狀態。
    • 當客戶端發起 add block 或 close 請求,若處於 FINALIZED 狀態的 replica 數量達到最小副本數要求,則切換到 COMPLETE 狀態
    • 若發生 block recovery,狀態切換到 UNDER_RECOVERY
  • UNDER_RECOVERY,有三種狀況:
    • 0 字節長度的 replica 將直接被刪除。
    • 恢復成功,切換到 COMPLETE
    • NameNode 發生重啓,全部打開文件的最後一個 block 會恢復成 UNDER_CONSTRUCTION 狀態。
  • COMMITTED 出發,有兩種狀況:
    • 若處於 FINALIZED 狀態的 replica 數量達到最小副本數要求或者文件被強制關閉或者 NameNode 重啓且不是最後一個 block,
      則直接切換爲 COMPLETE 狀態。
    • NameNode 發生重啓,全部打開文件的最後一個 block 會恢復成 UNDER_CONSTRUCTION 狀態。
  • COMPLETE 出發,只有在 NameNode 發生重啓,其打開文件的最後一個 block 會恢復成 UNDER_CONSTRUCTION 狀態。
    這種狀況,若 Client 依然存活,有 Client 來關閉文件,不然由 lease recovery 過程來恢復(詳見下文)。

理解了 block 和 replica 的狀態及其變化過程,咱們就能夠進一步詳細分析上述簡要說起的幾種自動恢復模式。

Lease Recovery 和 Block Recovery

前面講了 lease recovery 的目的是當 Client 在寫入過程當中掛了後,通過必定的超時時間後,收回租約並關閉文件。但在收回租約關閉文件前,須要確保文件 block 的多個副本數據一致(分佈式環境下不少異常狀況均可能致使多個數據節點副本不一致),若不一致就會引入 block recovery 過程進行恢復。下面是整個恢復處理流程的簡要算法描述:

  1. 獲取包含文件最後一個 block 的全部 DataNodes。
  2. 指定其中一個 DataNode 做爲主導恢復的節點。
  3. 主導節點向其餘節點請求得到它們上面存儲的 replica 信息。
  4. 主導節點收集了全部節點上的 replica 信息後,就能夠比較計算出各節點上不一樣 replica 的最小長度。
  5. 主導節點向其餘節點發起更新,將各自 replica 更新爲最小長度值,保持各節點 replica 長度一致。
  6. 全部 DataNode 都同步後,主導節點向 NameNode 報告更新一致後的最終結果。
  7. NameNode 更新文件 block 元數據信息,收回該文件租約,並關閉文件。

其中 3~6 步就屬於 block recovery 的處理過程,這裏有個疑問爲何在多個副本中選擇最小長度做爲最終更新一致的標準?想一想寫入流水線過程,若是 Client 掛掉致使寫入中斷後,對於流水線上的多個 DataNode 收到的數據在正常狀況下應該是一致的。但在異常狀況下,排在首位的收到的數據理論上最多,末位的最少,因爲數據接收的確認是從末位按反方向傳遞到首位再到 Client 端。因此排在末位的 DataNode 上存儲的數據都是實際已被確認的數據,而它上面的數據實際在不一致的狀況也是最少的,因此算法裏選擇多個節點上最小的數據長度爲標準來同步到一致狀態。

Pipeline Recovery

這裏寫圖片描述
如上圖所示,pipeline 寫入包括三個階段:

  1. pipeline setup:Client 發送一個寫請求沿着 pipeline 傳遞下去,最後一個 DataNode 收到後發回一個確認消息。Client 收到確認後,pipeline 設置準備完畢,能夠往裏面發送數據了。
  2. data streaming:Client 將一個 block 拆分爲多個 packet 來發送(默認一個 block 64MB,太大因此須要拆分)。Client 持續往 pipeline 發送 packet,在收到 packet ack 以前容許發送 n 個 packet,n 就是 Client 的發送窗口大小(相似 TCP 滑動窗口)。
  3. close:Client 在全部發出的 packet 都收到確認後發送一個 Close 請求,
    pipeline 上的 DataNode 收到 Close 後將相應 replica 修改成 FINALIZED 狀態,並向 NameNode 發送 block 報告。NameNode 將根據報告的 FINALIZED 狀態的 replica 數量是否達到最小副本要求來改變相應 block 狀態爲 COMPLETE

Pipeline recovery 能夠發生在這三個階段中的任意一個,只要在寫入過程當中一個或多個 DataNode 遭遇網絡或自身故障。咱們來分別分析下。

從 pipeline setup 錯誤中恢復

在 pipeline 準備階段發生錯誤,分兩種狀況:

  1. 新寫文件:Client 從新請求 NameNode 分配 block 和 DataNodes,從新設置 pipeline。
  2. 追加文件:Client 從 pipeline 中移除出錯的 DataNode,而後繼續。

從 data streaming 錯誤中恢復

  1. 當 pipeline 中的某個 DataNode 檢測到寫入磁盤出錯(多是磁盤故障),它自動退出 pipeline,關閉相關的 TCP 鏈接。
  2. 當 Client 檢測到 pipeline 有 DataNode 出錯,先中止發送數據,並基於剩下正常的 DataNode 從新構建 pipeline 再繼續發送數據。
  3. Client 恢復發送數據後,從沒有收到確認的 packet 開始重發,其中有些 packet 前面的 DataNode 可能已經收過了,則忽略存儲過程直接傳遞到下游節點。

從 close 錯誤中恢復

到了 close 階段纔出錯,實際數據已經所有寫入了 DataNodes 中,因此影響很小了。Client 依然根據剩下正常的 DataNode 重建 pipeline,讓剩下的 DataNode 繼續完成 close 階段須要作的工做。

以上就是 pipeline recovery 三個階段的處理過程,這裏還有點小小的細節可說。
當 pipeline 中一個 DataNode 掛了,Client 重建 pipeline 時是能夠移除掛了的 DataNode,也可使用新的 DataNode 來替換。這裏有策略是可配置的,稱爲 DataNode Replacement Policy upon Failure,包括下面幾種狀況:

  1. NEVER:從不替換,針對 Client 的行爲
  2. DISABLE:禁止替換,DataNode 服務端拋出異常,表現行爲相似 Client 的 NEVER 策略
  3. DEFAULT:默認根據副本數要求來決定,簡單來講若配置的副本數爲 3,若是壞了 2 個 DataNode,則會替換,不然不替換
  4. ALWAYS:老是替換

總結

本文講述了 HDFS 異常處理與恢復的處理流程和細節,它確保 HDFS 在面對網絡和節點錯誤的狀況下保證數據寫入的持久性和一致性。讀完本篇相信你會對 HDFS 內部的一些設計和工做狀態有更深的認識,本文特意抽象的描述了一些涉及具體算法的部分。由於 HDFS 做爲一個開源軟件還在不斷髮展演變,具體算法代碼實現總會變化,但設計理念和 design for failure 的設計思考要點的持續性要長久的多,會更具參考價值。若是你還想對某些特定細節的實現作進一步的瞭解,只能去深刻閱讀對應部分的代碼,這沒法經過閱讀文檔或相關文章來得到,這也是代碼開源的價值所在。

參考

[1] Hadoop Documentation. HDFS Architecture.
[2] Robert Chansler, Hairong Kuang, Sanjay Radia, Konstantin Shvachko, and Suresh Srinivas. The Hadoop Distributed File System
[3] Tom White. Hadoop: The Definitive Guide. O’Reilly Media(2012-05), pp 94-96
[4] Yongjun Zhang. Understanding HDFS Recovery Processes
[5] Hairong
Kuang,
Konstantin
Shvachko,
Nicholas
Sze,
Sanjay
Radia,
 Robert
Chansler
, Yahoo!
HDFS
team
 Design Specification: Append/Hflush/Read
Design

[6] HDFSteam. Design Specification: HDFS Append and Truncates


下面是我本身開的一個微信公衆號 [瞬息之間],除了寫技術的文章、還有產品的、行業和人生的思考,但願能和更多走在這條路上同行者交流,有興趣可關注一下,謝謝。

相關文章
相關標籤/搜索