在NameNode中的Namespace管理層是負責管理整個HDFS集群文件系統的目錄樹以及文件與數據塊的映射關系。以下就是Namespace的內存結構:
以上是一棵文件目錄樹,可見Namespace本身其實是一棵巨大的樹。在這棵樹中INodeFile
表示文件,INodeDirectory
表示文件目錄。在HDFS中的實現中,INodeFile
和INodeDirectory
都是繼承INode
的,以下是INode
的繼承關系:
目錄樹數據結構詳細解釋
- INode
INode
是INodeFile
和INodeDirectory
父類,有一個關鍵的屬性,就是parent
,這個表示當前的INode的父親INode
,每一個文件或者文件目錄都會記錄它的父親節點,這樣根據這個父子關系就可以構建出一個文件目錄樹。這個目錄樹的根節點是/
- INodeWithAdditionalFields
這個類中包含了文件和目錄的共同的屬性,比如:唯一標識id
、INode
名稱、權限、修改時間、訪問時間等基礎信息。除常用基礎屬性外,其中還提供了擴展屬性features,如Quota、Snapshot等均通過Feature增加,如果以后出現新屬性也可通過Feature方便擴展
- INodeFile
INodeFile表示一個文件,除了繼承INode和INodeWithAdditionalFields中的屬性外,還有兩個文件特殊的屬性:
- header:標識存儲策略ID、副本數和數據塊大小的信息
- blocks:該文件包含的數據塊數組
- INodeDirectory
INodeDirectory則持有子節點的列表children。這里需要特別說明children是默認大小為5的ArrayList,按照子節點name有序存儲,雖然在插入時會損失一部分寫性能,但是可以方便后續快速二分查找提高讀性能,對一般存儲系統,讀操作比寫操作占比要高
Namespace內存估算
Namespace管理的文件目錄樹是存儲在NameNode的內存中的,這樣是為了提高訪問速度。那么我們怎么樣來估算Namespace管理的文件目錄樹占多大內存呢?我們下面從幾個關鍵數據結構所占的內存來估算。
目錄和文件結構在繼承關系中各屬性的內存占用情況如下表所示:
除圖中提到的屬性信息外,一些附加如ACL等非通用屬性,沒有在統計范圍內
以上每個對象所占內存的大小的估算是在64位操作系統上且沒有開啟指針壓縮功能場景下
根據前面的分析,假設HDFS目錄和文件數分別為1億,Block總量在1億情況下,整個Namespace在JVM中內存使用情況:
- Total(Directory) = (8 + 72 + 80) ∗ 100M + 8 ∗ num(total children)
- Total(Files) = (8 + 72 + 56) ∗ 100M + 8 ∗ num(total blocks)
- 內存總大小是:Total(Directory) + Total(Files)
上面為什么是乘以100M呢? 因為100M = 100 * 1024 * 1024 bytes = 104857600 bytes,約等於1億字節,而上面的內存的單位都是字節的,我們乘以100M,就相當於1億個目錄或者1億個文件了
從整個目錄樹的父子關系上看,num(total children)就是目錄節點數和文件節點數之和。num(total blocks)是1億。所以上面的場景的Namespace占用的總內存是: Total(Directory) + Total(Files) = (8 + 72 + 80) ∗ 100M + 8 * 200M + (8 + 72 + 56) ∗ 100M + 8 * 100M = 31.25G
Namespace在JVM堆內存空間中常駐,在NameNode的整個生命周期一直在內存存在,同時為保證數據的可靠性,NameNode會定期對其進行Checkpoint,將Namespace物化到外部存儲設備(也就是FSImage和EditsLog機制了)。隨着數據規模的增加,文件數/目錄樹也會隨之增加,整個Namespace所占用的JVM內存空間也會基本保持線性同步增加。