HDFS架構設計和讀寫流程

HDFS是一種分佈式文件系統,具有高度的容錯能力,旨在部署在低成本硬件上。

設計目標:

  • 考慮硬件故障,檢測故障並快速、自動地從故障中恢復是HDFS的核心目標。
  • HDFS設計用於批處理,而不是用戶交互使用。重點在於數據訪問的高吞吐量,而不是數據訪問的低延遲。
  • 大數據集。它應該提供較高的聚合數據帶寬,並可以擴展到單個羣集中的數百個節點。
  • 簡化了數據一致性模型。考慮應用場景出於簡化設計和實現的目的,HDFS假設了一種 write-once-read-many 的文件訪問模型。支持將內容追加到文件末尾,但不能在任意點更新。
  • 移動"計算"比移動數據便宜。將計算遷移到更靠近數據的位置,而不是將數據移動到應用程序正在運行的位置。
  • 跨異構硬件和軟件平臺的可移植性

HDFS架構

HSFS是以master/slave模式運行的,其中NameNode運行在master節點,DataNode運行slave節點。在內部,一個文件其實被分成一個或多個數據塊,這些塊存儲在一組Datanode上。

Namenode執行文件系統的Namespace操作,比如打開、關閉、重命名文件或目錄。它也負責確定數據塊到具體Datanode節點的映射。
Datanode負責處理文件系統客戶端的讀寫請求。在Namenode的統一調度下進行數據塊的創建、刪除和複製。

Namenode是所有HDFS元數據的仲裁者和管理者。羣集中單個NameNode的存在極大地簡化了系統的體系結構。

在這裏插入圖片描述

文件系統命名空間

HDFS支持傳統的分層文件組織(目錄結構)。用戶或應用程序可以創建目錄並將文件存儲在這些目錄中。文件系統名稱空間層次結構與大多數其他現有文件系統相似。可以創建和刪除文件,將文件從一個目錄移動到另一個目錄或重命名文件。

當前(hadoop 3.2.1),HDFS支持用戶磁盤配額和訪問權限控制。暫不支持硬鏈接或軟鏈接。

配額(Quota)

HDFS允許管理員爲使用的名稱數和用於單個目錄的空間量設置配額。

名稱配額是對以該目錄爲根結點的樹中,文件和目錄名稱數量的硬限制。如果超出配額,文件和目錄創建將失敗。

空間配額是對以該目錄爲根節點的樹中,文件所使用的字節數的硬性限制。如果配額不允許寫入整個塊,則塊分配失敗。塊的每個副本均計爲配額。

fsimage的兩種配額都是持久的。啓動時,如果fsimage立即違反配額(可能是fsimage被祕密修改),則會爲每個此類違反打印警告。
查看配額命令:hadoop fs -count -q [-h] [-v] [-t [以逗號分隔的存儲類型列表]] <目錄> … <目錄>
設置目錄空間配額命令:hdfs dfsadmin -setSpaceQuota <目錄> … <目錄>

訪問權限
HDFS 每個文件和目錄都與一個Owner和一個Group相關聯。對於文件,需要r權限才能讀取文件,而w權限才需要寫入或附加到文件。

數據複製

Block是HDFS的最小存儲單元。默認大小:128M(HDFS 1.x中,默認64M),若文件大小不足128M,則會單獨成爲一個block。實質上就是Linux相應目錄下的普通文件,名稱格式:blk_xxxxxxx。

HDFS將每個文件存儲爲一系列塊(Block),複製文件的塊是爲了容錯。支持機架的副本放置策略的目的是提高數據可靠性,可用性和網絡帶寬利用率。

塊大小和複製因子是每個文件可配置的。應用程序可以指定文件的副本數。複製因子可以在文件創建時指定,以後可以更改。
HDFS中的文件只能寫入一次(追加和截斷除外),並且在任何時候都只能具有一個Writer。

在常見情況下,當複製因子爲3時,HDFS的放置策略是:如果Writer位於數據節點上,則將第一個副本放置在本地計算機上;否則,將第一個副本放入與Writer位於同一機架的隨機數據節點上;第二個副本放置在另一個不同機架上的一個結點上,最後一個副本放置在另一個機架的另一個結點上。使用此策略,文件的副本不會均勻分佈在機架上。三分之一的副本位於一個節點上,三分之二的副本位於同一個機架上,其餘三分之一則平均分佈在第二個機架上。

爲了最大程度地減少全局帶寬消耗和讀取延遲,HDFS嘗試滿足最接近Reader的副本的讀取請求。如果在與Reader節點相同的機架上存在一個副本,則該副本應優先滿足讀取請求。

元數據的持久化

NameNode使用一個稱爲EditLog的事務日誌來永久記錄文件系統元數據發生的每個更改。
NameNode使用其本地主機OS文件系統中的文件來存儲EditLog。整個文件系統Namespace(包括塊到文件的映射和文件系統屬性)存儲在名爲FsImage的文件中。FsImage作爲文件,存儲在NameNode的本地文件系統中

NameNode也在內存中保留整個文件系統名稱空間和文件Blockmap的鏡像Image。當NameNode啓動或由可配置的閾值觸發檢查點時,它會從磁盤讀取FsImage和EditLog,將EditLog中的所有事務應用於FsImage的內存中表示形式,並將此新版本刷新爲磁盤上的新FsImage。然後,它可以截斷舊的EditLog,因爲其事務已應用於持久性FsImage。此過程稱爲檢查點(checkpoint恢復)。

我們無需爲每個編輯修改FsImage,而是將編輯保留在Editlog中。在checkpoint期間,來自Editlog的更改將應用​​於FsImage。
可以在給定的時間間隔觸發檢查點(以秒錶示的dfs.namenode.checkpoint.period,或者在累積一定數量的文件系統事務之後(dfs.namenode.checkpoint.txns)。如果同時設置了這兩個屬性,則要達到的第一個閾值將觸發檢查點。

HDFS通信

按照設計,NameNode永遠不會啓動任何RPC。相反,它僅響應由DataNode或客戶端發出的RPC請求。

健壯性

HDFS的主要目標是即使出現故障也能可靠地存儲數據。常見的故障類型是NameNode故障,DataNode故障和網絡分區(network partitions)。

重新複製

每個DataNode定期向NameNode發送心跳消息。網絡分區(CAP理論中的分區概念)可能導致一部分DataNode失去與NameNode的連接。NameNode將沒有最近心跳的DataNode標記爲已死,並且不向其轉發任何新的IO請求。DataNode掛掉可能導致某些塊的複製因子降至其指定值以下。NameNode不斷跟蹤需要複製的塊,並在必要時進行重新複製

判斷DataNode失效的超時時間比較保守地長(默認情況下超過10分鐘),以避免由DataNode的狀態震盪引起的複製風暴。

集羣Rebalance
如果DataNode的可用空間低於某個閾值,可能會自動將數據從一個DataNode移至另一個DataNode。

數據完整性
從DataNode提取的數據塊可能會損壞。由於存儲設備故障,網絡故障或軟件故障,可能會導致這種損壞。客戶端創建HDFS文件時,它將計算文件每個塊的校驗和,並將這些校驗和存儲在同一HDFS命名空間中的單獨的隱藏文件中。客戶端檢索文件內容時,它將驗證從每個DataNode接收的數據是否與存儲在關聯的checksum文件中的校驗和匹配。

元數據磁盤故障
FsImage和EditLog的損壞可能導致HDFS實例無法正常運行。可以將NameNode配置爲支持維護FsImage和EditLog的多個副本。多個元數據副本的同步更新可能會降低NameNode的處理速度,好在數據密集型的HDFS元數據的量並不大。

快照
HDFS實現的快照非常高效,可以在文件系統的子樹上甚至整個文件系統上創建快照。快照的一些常見用例是數據備份,防止用戶錯誤和災難恢復。快照文件記錄了塊列表和文件大小,沒有數據複製。如果快照表目錄中有快照,則在刪除所有快照之前,不能刪除或重命名該目錄。
創建快照命令:hdfs dfs -createSnapshot <路徑> [<快照名稱>]

Ref:
https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html


HDFS讀文件過程簡介

在Hadoop中使用FileSystem.open()方法來創建輸入流。MapReduce在提交作業時,已經確定了每個map和reduce要讀取的文件,文件的偏移量和讀取的長度,所以MapReduce作業大部分是文件的隨機讀取。
HDFS在數據傳輸過程中,對數據塊不是按整個block進行傳輸的,而是將block切分成一個個的數據包DFSPacket進行傳輸。

  1. Client向namenode發RPC請求, 獲取要打開文件的blocks信息。
  2. Namenode返回文件的元信息(BlockId以及所在的Datanode列表)
  3. Client收到後和最近的DataNode建立連接,以packet爲單位依次傳輸對應的數據塊。
  4. Client反覆讀取各個Block塊,直到最後一個Block完成後,關閉和DataNode的連接。
    如果讀取某個block失敗, 它會去嘗試讀取存儲此block的其他DataNode,並向NameNode報告損壞的塊。

下圖所示:

在這裏插入圖片描述

HDFS寫文件過程簡介

Client會將文件數據緩存到一個臨時的本地文件中。當本地文件累積的數據至少具有一個HDFS塊大小時,才與NameNode建立連接。NameNode返回DataNode的數據流管道Pipline。
客戶端將塊寫入流水線中的第一個DataNode,同時將其傳遞給流水線中的下一個DataNode,將blockReceived消息發送給NameNode,並將確認消息發送給DFSOutputStream客戶端。

  1. Client調用FileSystem.create()創建文件
    首先會通過RPC調用,在NameNode上創建文件的元數據,構造輸出流outputStream。
  2. 首次寫入時,先向NameNode申請數據塊,NameNode在目錄樹的命名空間中創建一個空的新文件,並記錄在EditLog。分配數據塊成功後,連同這個塊的所有DataNode列表給客戶端。
  3. 通過Pipeline方式將數據分成一個個DFSPacket,在多個DataNode之間依次順序傳輸文件包。比如寫入Block5時,傳輸Block5給DN1,DN1傳給複製集中的DN2,DN2傳給DN3,DN3校驗後返回ack給DN2,DN2返回ack給DN1,最後DN1返回給客戶端寫入完成。
  4. 寫完一個數據塊後,數據流管道上的DataNode,會通過RPC向NameNode提交數據塊。如果還有等待輸出的數據,會再申請添加一個新的Block,直到所有數據塊傳輸完成。
  5. 關閉流的時候,DFSOutputStream通過RPC向NameNode提交所有的Block,所有DataNode都彙報完成後,NameNode確認當前文件寫完成。

如果管道流水線中的任何一個DataNode失敗,失敗的Stream會被關閉。數據將會繼續寫到剩餘的DataNode中。同時NameNode會被告知待備份狀態,繼續備份數據到新的可用的節點,實現容錯。

參考下圖
在這裏插入圖片描述

。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章