Hadoop重點難點:HDFS讀寫NN2NNDN

- Hadoop NameNode詳解 -

NameNode在記憶體中儲存著整個檔案系統的名字空間和檔案資料塊的地址對映(Blockmap)。如果NameNode宕機,那麼整個叢集就癱瘓了。

整個HDFS可儲存的檔案數受限於NameNode的記憶體大小。

這個關鍵的元資料結構設計得很緊湊,因而一個有4G記憶體的Namenode就足夠支撐大量的檔案和目錄。

NameNode負責:檔案元資料資訊的操作以及處理客戶端的請求

NameNode管理:HDFS檔案系統的名稱空間NameSpace。

NameNode維護:檔案系統樹(FileSystem)以及檔案樹中所有的檔案和資料夾的元資料資訊(matedata),維護檔案到塊的對應關係和塊到節點的對應關係

NameNode檔案:namespace映象檔案(fsimage),操作日誌檔案(edit log),這些資訊被Cache在RAM中,當然這兩個檔案也會被持久化儲存在本地硬碟。

NameNode記錄:每個檔案中各個塊所在的資料節點的位置資訊。但它並不永久儲存塊的位置資訊,因為這些資訊在系統啟動時由資料節點重建。從資料節點重建:在nameNode啟動時,DataNode向NameNode進行註冊時傳送給NameNode。

NN和SecondaryNameNode(2NN)工作機制

思考:NameNode中的元資料是儲存在哪裡的?

首先,我們做個假設,如果儲存在NameNode節點的磁碟中,因為經常需要進行隨機訪問,還有響應客戶請求,必然是效率過低。因此,元資料需要存放在記憶體中。但如果只存在記憶體中,一旦斷電,元資料丟失,整個叢集就無法工作了。因此產生在磁碟中備份元資料的FsImage。

這樣又會帶來新的問題,當在記憶體中的元資料更新時,如果同時更新FsImage,就會導致效率過低,但如果不更新,就會發生一致性問題,一旦NameNode節點斷電,就會產生資料丟失。

因此,引入Edits檔案(只進行追加操作,效率很高)。每當元資料有更新或者新增元資料時,修改記憶體中的元資料並追加到Edits中。這樣,一旦NameNode節點斷電,可以透過FsImage和Edits的合併,合成元資料。

但是,如果長時間新增資料到Edits中,會導致該檔案資料過大,效率降低,而且一旦斷電,恢復元資料需要的時間過長。因此,需要定期進行FsImage和Edits的合併,如果這個操作由NameNode節點完成,又會效率過低。因此,引入一個新的節點SecondaryNamenode,專門用於FsImage和Edits的合併。

Fsimage:NameNode記憶體中元資料序列化後形成的檔案。

Edits:記錄客戶端更新元資料資訊的每一步操作(可透過Edits運算出元資料)。

NameNode啟動時,先滾動Edits並生成一個空的edits。inprogress,然後載入Edits和Fsimage到記憶體中,此時NameNode記憶體就持有最新的元資料資訊。Client開始對NameNode傳送元資料的增刪改的請求,這些請求的操作首先會被記錄到edits。inprogress中(查詢元資料的操作不會被記錄在Edits中,因為查詢操作不會更改元資料資訊),如果此時NameNode掛掉,重啟後會從Edits中讀取元資料的資訊。然後,NameNode會在記憶體中執行元資料的增刪改的操作。

由於Edits中記錄的操作會越來越多,Edits檔案會越來越大,導致NameNode在啟動載入Edits時會很慢,所以需要對Edits和Fsimage進行合併(所謂合併,就是將Edits和Fsimage載入到記憶體中,照著Edits中的操作一步步執行,最終形成新的Fsimage)。SecondaryNameNode的作用就是幫助NameNode進行Edits和Fsimage的合併工作。

SecondaryNameNode首先會詢問NameNode是否需要CheckPoint(觸發CheckPoint需要滿足兩個條件中的任意一個,定時時間到和Edits中資料寫滿了)。直接帶回NameNode是否檢查結果。SecondaryNameNode執行CheckPoint操作,首先會讓NameNode滾動Edits並生成一個空的edits。inprogress,滾動Edits的目的是給Edits打個標記,以後所有新的操作都寫入edits。inprogress,其他未合併的Edits和Fsimage會複製到SecondaryNameNode的本地,然後將複製的Edits和Fsimage載入到記憶體中進行合併,生成fsimage。chkpoint,然後將fsimage。chkpoint複製給NameNode,重新命名為Fsimage後替換掉原來的Fsimage。NameNode在啟動時就只需要載入之前未合併的Edits和Fsimage即可,因為合併過的Edits中的元資料資訊已經被記錄在Fsimage中。

HDFS 程序各自的職責以及如何協調工作

為了便於理解,舉個簡單的例子:

我們可以把整個 HDFS 系統看作是動物園, 裡面有許多嗷嗷待哺的小動物(DataNode),管理員們(Client)負責投餵和收餐盤(讀寫資料),但是動物太多,管理員沒法準確知道需要去哪個位置投餵,所以需要有一箇中轉站,中轉站裡有一張記錄小動物位置和種類資訊(元資料資訊)的表格(NameNode)放在固定的地方,投餵前管理員們都來中轉站看一下表格,然後去找具體的位置。

表格的資料很重要,如果丟了管理員就會手足無措了,所以我們將表格再備份一份(Standby NameNode)放在抽屜裡,每次資料修改都將資料同步到備份的表格處(JournalNode),如果主表格丟失了,監控(ZKFC)發現後保安(Zookeeper)立馬把備份表格拿出來使用。

Hadoop重點難點:HDFS讀寫/NN/2NN/DN

上面的例子幫助大家簡單理了一下邏輯,下面進入正題,HDFS 程序還是比較多的,每個程序有各自的職責,然後按照既定的規則與其他程序互動,目的是維持系統正常穩定地執行。

Active NameNode:

它是 HDFS 對外提供讀寫服務的唯一 Master 節點,管理著檔案系統的 Namespace;維護著檔案的元資料,包括

檔名、副本數、檔案的 BlockId 以及 Block 所在的伺服器

等資訊;同時會接受來自 Client 端的讀寫請求,和接受 DataNode 的 Block 資訊上報。

Standby NameNode:

Active NameNode 的備用節點,它會及時從 JournalNode 中讀取 EditLog 資料並更新記憶體,以

保證當前狀態儘可能與主節點同步

。需要注意的是,叢集中最多一臺處於 Active 狀態,最多一臺處於 Standby 狀態。

JournalNode Cluster:

用於主備 NameNode 之間共享 Editlog 的一致性共享儲存系統。

負責儲存 Editlog 以及將元資料從主節點實時同步到備用節點

。流程是主節點先將 EditLog 檔案 push 進 JournalNode,備節點再從 JournalNode 節點 Pull 資料,

JournalNode 不主動進行資料交換

。叢集由 2N + 1 個 JournalNode 程序組成,可以容忍最多 N 臺 JournalNode 節點掛掉。

ZKFailoverController(ZKFC):

ZKFailoverController 以獨立程序執行,正常情況每個 ZKFC 都會監控自己負責的 NameNode 的心跳,如果異常,則會斷開與 ZooKeeper 的連線,釋放分散式鎖,另外一個 NameNode 上的 ZKFC 則會獲取鎖,然後會把對應的 NameNode 的狀態從 Standby 切換到 Active。ZKFC 主要負責:

NameNode 健康狀況檢測;藉助 Zookeeper 實現 NameNode 自動選主;操作 NameNode 進行主從切換。

Zookeepe

r:為 ZKFC 實現自動選主功能提供統一協調服務。透過 watcher 監聽機制,通知 ZKFC 異常 NameNode 的下線;保證同一時刻只有一個 Active Name 節點,並告知客戶端。

DataNode:

負責實際資料的儲存,在如圖 NameNode 高可用的架構下,DataNode 會

同時向主備兩個 NameNode 節點進行元資料上報

,但是僅執行主節點下發的指令。

另外,secondNameNode 由於會有單點問題已經很少應用,這裡不討論了,他的工作可以被 JournalNode 取代。

相關面試題

HDFS 客戶端與 NameNode 和 DataNode 的通訊和互動過程?

Secondary NameNode 的功能在高可用架構下被那個程序所取代?

NameNode 會儲存哪些資料?

HDFS 是如何保證 NameNode 高可用的?

ZKFC 是如何實現主節點異常切換的?

Zookeeper 在異常切換中起到的作用?

這些問題都是程序的職責相關的內容,答案都在上面的知識點中都有詳細描述,問的方式有千千萬萬種,但是萬變不離其宗,背後的知識點都是固定的。這裡考察的是對 HDFS 元件比較常規的瞭解,各元件的職責和工作方式,重點需要掌握的是 ZKFC 和 JournalNode 的作用,不容有失。

Block & Packet & Chunk

Block

:HDFS 中的檔案在物理上是分塊儲存,即 Block。每個 Block 大小:Hadoop 2。x 版本為 128 MB;Hadoop 1。x 版本為 64 MB 。

Packet

:Packet 是 Client 端向 DataNode,或 DataNode 的 PipLine 之間傳資料的基本單位,預設 64KB。

Chunk

:Chunk 是最小的單位,它是 Client 向 DataNode 或 DataNode 的 PipLine 之間進行資料校驗的基本單位,預設 512Byte,因為用作校驗,故每個 chunk 需要帶有 4 Byte 的校驗位。所以實際每個 chunk 寫入 packet 的大小為 516 Byte。

為什麼 Block 會從老版本的 64MB 升級到 128MB?

以前伺服器的配置大多是 5400 轉硬碟,平均讀寫理論上在 60-90MB/s,現在常用的是 7200 轉,讀寫大致在 130-190MB/s。資料寫磁碟是需要消耗定址時間的,最優的理論情況是定址時間僅佔傳輸時間的 1%,為了降低定址開銷,最優情況當然是一次定址寫完這次傳輸的所有資料。

安全模式

安全模式是 HDFS 處於初始化狀態的一種自我保護機制,在這種模式下,

HDFS 對於客戶端來說是隻讀的

。NameNode 主節點啟動時,HDFS 就會進入安全模式。在安全模式中,

DataNode 會向 NameNode 上報可用 Block 的資訊,即告知 NameNode 當前叢集中有哪些可用資料

,因為 NameNode 初始時本身是不知道叢集中有哪些 Block 的。

退出安全模式的條件?

HDFS 可用 Block 佔總數的比例(dfs。namenode。safemode。threshold-pct):預設 99。9%

可用的資料節點數量符合要求(dfs。namenode。safemode。min。datanodes):預設 0,即無要求。

滿足上面兩個條件的持續時間(dfs。namenode。safemode。extension):預設 1ms,即維持正常狀態 1ms 就退出安全模式。

原始碼級客戶端讀寫資料互動流程

資料讀寫流程在面試過程中是最常見的問題,答案其實很簡單,但是想要答得出彩其實是不容易的,如這個知識點所描述的,從原始碼級別去看待這個讀寫流程,能看得更透徹,給面試官的印象也會更深刻。

寫資料流程?

Hadoop重點難點:HDFS讀寫/NN/2NN/DN

首先 client 端透過在 DistributedFileSystem 上呼叫 create() 方法來建立一個檔案。其中,DistributedFileSystem 是客戶端使用者建立的一個物件,使用者可以透過呼叫 DistributedFileSystem 的方法來對 HDFS 做讀寫操作。

在收到 client 端 create 動作之後,DistributedFileSystem 透過 RPC 與 NameNode 通訊 ,讓它在檔案系統的 namespace 上建立一個獨立的新檔案,NameNode 會確認

檔案是否已經存在

以及

客戶端是否有許可權

。確認成功後,NameNode 會

生成一條新檔案的記錄

並返回一個

負責 client 端與 datanode 和 namenode 進行 I/O 操作的 DFSOutputStream 物件

給客戶端,另外還會包含可寫入的 DataNode 的資訊。如檔案建立失敗,客戶端會丟擲一個 IOException。

當客戶端開始寫資料,DFSOutputStream 將檔案

分割成很多很小的塊

,然後將每個小塊放進一個個 package 中, packages 會寫進一個

內部佇列

中,準備往 DataNode 寫資料。

此時會根據 NameNode 返回的可寫入的 DataNode 列表來

構成一個 pipeline

,預設是有三個 DataNode 組成, DataStreamer 將能夠組成塊的包先流入 pipeline 中的第一個 DataNode ,第一個 DataNode 會先儲存來到的包,然後繼續將所有的包轉交到 pipeline 中的第二個 DataNode 中,以此類推。

DFSOutputStream 還維護了另一個

確認佇列

,該佇列

等待 DataNode 的寫入確認

。當一個包已經被 pipeline 中的所有 DataNode 確認了寫如磁碟成功,這個包才會從確認佇列中移除。

當 Client 完成了資料寫入,會在流上呼叫 close() 方法。需注意,這一步不是在 4 和 5 第一次完成之後,而是客戶端

需要寫入的所有資料寫完了

之後。這個行為會將所有剩下的包 flush 進 DataNode 中。

等到確認資訊全部都到達,即步驟 5 完成之後,Client 會再次與 NameNode 通訊告知完成。

NameNode 會確認該檔案的備份數是否滿足要求

讀資料流程?

Hadoop重點難點:HDFS讀寫/NN/2NN/DN

與寫流程類似,第一步 Client 端同樣是呼叫 DistributedFileSystem 物件,指定想要讀的檔案 target。txt ,使用 open() 方法。

此時 DistributedFileSystem 就會與 NameNode 進行 RPC 通訊,獲取組成 target。txt 的 block 資訊,其中包含** block 存在於哪些 DataNode 中**。

然後 Client 就呼叫 read() 方法,這裡同樣會有一個 DFSInputStream 來負責與 DataNode 的 IO。此時會找到 DataNode 列表裡離當前 Client 端最近的一個 DataNode(

如何判斷最近,後面的機架感知中會解釋

)。

然後 DFSInputStream 就透過

重複呼叫 read() 方法

,資料就從 DataNode 流動到了客戶端。當該 DataNode 中最後一個塊的讀取完成了, DFSInputStream 會關閉與 DataNode 的連線,然後為下一塊尋找最佳節點。

這個過程對客戶端來說是透明的

,在客戶端那邊看來,就像是隻讀取了一個連續不斷的流。

當客戶端完成了讀取,就會呼叫 close() 方法結束整個流程。**

Hadoop重點難點:HDFS讀寫/NN/2NN/DN

- HDFS可用性保證機制 -

之前提到了 HDFS 是由 DataNode,NameNode,JournalNode,DFSZKFailoverController 這些元件組成的,所以可用性會涉及到各個元件,面試時也要從多個方面來回答這個問題。

首先

NameNode

的高可用由 JournalNode 和 DFSZKFailoverController 保證,NameNode 有主備兩個節點,

JournalNode 負責主備節點資料的同步保證資料一致性,ZKFC 負責主備的 Failover

,即主節點宕機由備用節點接替主節點工作。

JournalNode

也是分散式的,因為有

選舉機制

,所以預設要大於一的奇數個伺服器線上,同樣是具有可用性保證的。

DFSZKFailoverController

(ZKFC)是部署在兩個 NameNode 節點上的獨立的程序,他的作用是輔助 zookeeper 做 NameNode 的健康監控,保證異常切換,而 **Zookeeper **是一個獨立的分散式系統,用於管理和協調分散式系統的工作,它本身也會透過 zab 協議來保證資料一致,和主備節點的選舉切換等機制來保證可用性。

DataNode

節點的宕機會造成部分 block 的丟失,但是 block 一般都會有三個備份,且在不同的 DataNode,所以 DataNode 掛掉兩臺仍然能保證資料的完整性,同時

NameNode 會負責副本數的補充

對於

資料

的可用性保證,HDFS 還提供了

資料完整性校驗的機制

,當客戶端建立 HDFS 檔案時,它會計算檔案的每個塊的

校驗和

(checknums),並存儲在 NameNode 中。當客戶端讀檔案時,會驗證從每個 DataNode 接收的資料是否與 checknums 匹配。如果匹配失敗,則證明資料已經損壞,此時客戶端會選擇從其他 DataNode 獲取該塊的其他可用副本。