5000字12張圖講解nn記憶體中的元資料資訊

語言: CN / TW / HK

namenode作為hdfs中的元資料的管理模組,免不了會提到元資料包括哪些?在記憶體中又是如何儲存管理的,本文就來聊聊nn記憶體中的元資料資訊。


【整體概況】


在HDFS中,NN的主要作用是元資料管理,包括檔案系統目錄樹的管理、block塊的管理、以及dn結點管理


在具體實現中,FSDirectory對應檔案系統目錄樹的管理,包括檔案系統中各個目錄、檔案資訊記錄,以及彼此之間的層級關係;


DatanodeManager管理所有註冊的dn結點資訊,同時根據dn的心跳資訊、塊彙報更新相關的資訊記錄。


BlockManager則是nn中最龐大的一部分,內部又拆分成多個類來儲存管理塊的不同資訊和狀態。例如使用blocksmap儲存所有的塊資訊,使用underReplicatedBlocks、PendingReplicationBlocks來保證塊的副本數始終滿足指定個數。


雖然這幾個部分分別管理不同的元資料資訊,但彼此並不是孤立的,而是相互聯絡在一起的,例如:


  • 一個檔案中需要知道自身資料儲存在哪些塊中,具體實現中以一個數組記錄該檔案包含的所有塊資訊,因此檔案和塊就關聯起來了

  • 同樣,一個塊的副本分別儲存在哪個dn結點的哪個卷目錄中,也需要記錄下來。這樣,當dn結點出現異常,或者dn結點上的卷出現異常時,nn需要對儲存在這個dn結點上的塊進行副本的遷移保證塊的副本數滿足指定個數。這樣,塊資訊就和dn的儲存資訊關聯起來了。

下面就這幾部分內容分別展開說明。


【檔案系統目錄樹】


在HDFS檔案系統中,一個目錄或者一個檔案,都以一個INode來表示,並且都具有一些相同的屬性,例如許可權、所屬使用者/使用者組、最後修改時間、最後訪問時間等;但兩者又有一些不同的地方,例如目錄下可以有子目錄或檔案,而檔案則沒有這些東西,但檔案是有實際儲存的資料的,這些資料儲存在一個或多個塊中


在具體實現中:

  • 類INodeDirectory對應儲存檔案系統中的目錄資訊,INodeFile對應儲存檔案系統中的檔案資訊;

  • 兩個類均繼承自抽象類INodeWithAdditionFields,在這個抽象類中,定義了一些通用的屬性(檔案/目錄的名字、許可權、等),feature介面陣列包含目錄或檔案一些擴充套件的特性,諸如配額、快照、訪問控制、擴充套件屬性等;

  • 抽象類INode是所有檔案/目錄的父類,該類中有一個類成員parent記錄檔案/目錄的父節點,一方面是可以快速找到每個目錄/檔案的父目錄,另一方面是這些資訊會持久化到fsimage中,啟動時會根據記錄的parent欄位,重新在記憶體中構造出一顆完整的檔案目錄樹;

  • 在INodeDirectory中,類成員child是一個INode的集合,記錄該目錄下的子目錄(不包含遞迴子目錄)和檔案

  • 在INodeFile中,類成員blocks是一個BlockInfo的陣列,記錄該檔案的內容對應儲存的塊資訊,即檔案內容由哪些塊組成

  • FSDirectory類記錄整個檔案系統目錄樹的資訊,其類成員rootDir對應檔案系統中的根目錄("/"),類成員inodemap是INodeMap類的例項物件,內部的map包含了所有的inode資訊。



對相關類有了瞭解後,檔案系統目錄樹層級結構和記憶體中相關類的對應關係就能比較清晰的描述出來了。



【dn資訊的儲存】


dn節點資訊的幾個基礎類(如下圖所示):



  • DatanodeID

    記錄了datanode的一些基本資訊,包括唯一標示dn節點的ID(uuid)、dn的rpc通訊ip,埠、用於資料傳輸的ip,埠等(圖中沒有列出全部欄位)。

  • DatanodeInfo

    繼承自DatanodeID,擴充套件記錄了dn的可用容量、當前已使用容量、用於資料傳輸的執行緒數等資訊。

  • DatanodeDescriptor

    繼承自DatanodeInfo,記錄了該dn上不同卷目錄下儲存的塊資訊、異常卷目錄的個數及概況、以及nn需要通過心跳下發給dn的一些任務資訊,這些任務按不同型別分別儲存,例如需要進行塊複製的任務;塊恢復的任務;塊刪除的任務。

  • DatanodeStorageInfo

    dn節點某個卷的詳細資訊,包括卷的狀態、儲存ID、儲存型別(ssd,hdd等)、儲存容量、hdfs資料儲存佔用空間、非hdfs資料儲存佔用空間、以及儲存在該卷下的所有塊組成的雙向連結串列的表頭等。


  • DatanodeManager

    如字面意思,dn節點資訊的管理類內部以一個map表儲存所有dn節點的資訊,網路拓撲架構,以及一些其他用於節點管理控制的資訊


【block的儲存】


塊資訊的儲存主要包括幾個基礎類(如下圖所示):



  • Block

    用來描述記錄一個塊資訊,算是所有塊資訊描述的基類。自身實現了writable介面,可以進行序列化,方便在rpc中進行傳輸。

  • BlockInfo

    塊資訊的補充描述,繼承自Block類,但本身是一個抽象類

    nn內部大部分資料結構中都以BlockInfo作為基礎,儲存塊的相關資訊

    幾個重要的類成員:replication記錄塊建立時指定的副本數;triplets記錄了塊的所有副本對應儲存的dn資訊;uc則記錄了塊的狀態以及塊所有副本的狀態。

  • BlockInfoContigous

    繼承自BlockInfo,由於BlockInfo是一個抽象類,因此在nn的記憶體中,真正儲存塊資訊的是該類的例項物件及引用。

  • BlockUnderConstructionFeature

    描述了塊的狀態、以及塊所有副本的資訊

  • ReplicaUnderConstruction

    塊的副本的資訊,包括副本具體儲存的dn卷資訊,副本的狀態等。


上面幾個類的作用主要就是用來描述nn中儲存的塊資訊,在這個基礎上,還定義了一些類用於塊的儲存和管理:

1)BlocksMap

內部以一個LightWeightGSet(本質上是一個雜湊表)儲存所有的塊資訊



2)CorruptReplicasMap

內部以巢狀map的方式存放損壞的塊資訊,其中key為Block,表示是哪個塊;

value同樣為一個map,巢狀map中的key為塊所屬dn節點,value為損壞的原因,例如缺少時間戳、缺少長度、無效的狀態、客戶端或dn上報等。



3)InvalidateBlocks

存放無效的、待刪除的塊資訊。內部同樣定義了一個map,其中key為DatanodeInfo,value為儲存在該dn節點上的BlockInfo集合。

4)UnderReplicatedBlocks

存放不滿足副本數的塊資訊,內部一個數組加連結串列的方式來儲存塊資訊。



5)PendingReplicationBlocks

當前正在進行塊複製的塊資訊、以及塊複製任務超時的塊資訊




【block的狀態變化】


上面提到了塊的狀態,一個塊的生命週期中會有如下幾個狀態:

  • underConstruction(簡稱uc)

  • committed

  • complete

  • underRecovery

初始狀態為underConstruction,然後依次經過committed,最終變為complete。

underRecovery則用於塊恢復。


一個塊由一個或多個副本組成,每個副本也有自己的狀態,副本的狀態包括:

  • RBW(Replica Being Write)

  • RWR(Replica Waiting to be Recovered)

  • RUR(Replica is Under Recovery)

  • FINALIZED

  • TEMPORARY

對於正常寫流程,副本的狀態只會從RBW轉換為FINALIZED。


在完整的寫流程中,一個塊的狀態及其副本的狀態的可能變化如下圖所示:



1. 客戶端請求一個新的塊時,nn在內部為其分配一個塊,塊的初始狀態為uc,此後為該block近一步選擇副本儲存的dn結點,最後在記憶體中依次將塊的副本狀態初始化為RBW


2. 此後客戶端向dn建立連線,並開始寫block資料,在寫的過程中,dn會通過增量塊彙報向nn上報塊的狀態,包括正在接收塊(receiving)和已經接收完成(received),對於已經接收完成的塊,nn在處理時,會找到塊對應的副本,將其狀態置為finalized


3. 客戶端寫完一個塊後(收到dn結點的ack響應),會繼續向nn申請新的塊,在申請新的塊請求時,請求中會攜帶前一個塊的資訊nn在處理請求時,預設對前一個塊執行commit動作,即將塊的狀態設定為committed。此後,再近一步判斷,當前塊副本的狀態為finalized的個數是否達到建立時指定的副本數,如果是,則將塊的狀態設定為complete


4. 隨著更多副本儲存的dn結點進增量塊彙報後,該塊的副本數都處於finalized狀態,這時,如果塊的狀態為committed,nn會將塊的狀態置為complete。也就是說,只有客戶端對隱式對塊進行committed後,你能在處理dn的譖量塊彙報過程中也會觸發狀態的切換


5. 上面講到了nn的最終狀態為complete,但圖中箭頭最後指向的blockInfo中狀態卻是null。這就是程式碼中的一個小技巧了,直接將物件置為null來表示塊的complete狀態,因為complete已經是最終狀態了,在記憶體中再開闢記憶體空間記錄其狀態沒有意義,索性直接置為null。這對於需要儲存幾千萬到億級別的塊資訊來說,無疑節省了不少記憶體


【block與dn的關聯】


在上面提到的DatanodeStorageInfo類中,有一個blockList的欄位,該欄位是BlockInfo的一個例項物件,表示在該捲上儲存的一個塊資訊


而BlockInfo中的triplets欄位是一個物件陣列陣列長度為塊副本數✖️3即每個副本佔用3個位置,分別記錄該副本所在的卷資訊(DatanodeStroageInfo),以及前一個塊資訊,後一個塊資訊。


如上圖所示,DatanodeStorageInfo中的blockList作為連結串列的表頭,然後通過triplets中每個副本佔用的兩個位置(prev,next),將儲存在該卷中的塊串聯起來,形成一個雙向連結串列。


這樣在nn內部不僅可以快速知道一個塊儲存在哪個dn的哪個卷目錄下,還可以快速獲取一個dn的一個卷目錄下儲存了哪些塊。


這對於dn節點的上下線,或者dn節點的卷目錄異常後的塊恢復很重要,方便快速定位哪些塊需要進行塊複製或者塊恢復。


【總結】


本文主要講解了nn中幾個元資料資訊在記憶體中如何進行儲存的,包括檔案系統的目錄樹結構、datanode節點資訊、塊資訊。


當然還有也很多沒有展開的,例如,nn內部如何保證塊的副本數始終維持在指定個數的,即塊副本數的監測,塊複製、塊刪除任務的觸發執行,以及塊是如何恢復的,還有一些知識點沒有提到,例如dn節點的網路拓撲,機架感知等。後續再單獨進行講解。。


好了,本文就介紹到這裡,如果覺得本文對你有些幫助,來個點贊,在看吧,也歡迎分享轉發, 謝謝~



本文分享自微信公眾號 - hncscwc(gh_383bc7486c1a)。
如有侵權,請聯絡 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。