HDFS(Hadoop Distributed File System)是一個分佈式文件系統,它具備很高的容錯性,能夠支持運行在廉價的硬件設備上。HDFS具備很高的吞吐量,很是適合擁有海量數據的application。HDFS放寬了一些POSIX要求,以支持流的方式訪問文件系統中的數據。HDFS是Apache Hadoop核心項目的一部分。html
一個HDFS實例能夠包含成百上千臺的服務器,每一個服務器存儲整個文件系統數據的一部分。每臺服務器都有必定的機率出現硬件故障,這意味着一個HDFS實例中的一部分服務器在某個時刻多是不可用的。所以,發現故障並可以從故障中快速恢復是HDFS的核心目標。node
HDFS適合應用在批處理領域,而不適合與用戶交互的領域。它的設計目標是數據訪問的高吞吐量,而不是低延遲。服務器
運行在HDFS上的application一般都擁有大數據集,例如一個文件能夠達到gigabytes或terabytes大小。所以一個HDFS集羣能夠擴展到數百個節點,能夠支持數千萬個文件的存儲。網絡
HDFS上的文件模型是write-once-read-many。一個文件在完成建立-寫入數據-關閉後只能再對文件執行appends和truncates兩種類型修改操做,沒法再執行其它形式的修改。即只能在文件尾部添加或刪除數據,不支持在文件的任意位置修改數據。數據結構
在分佈式系統中,若是一個計算節點要處理的數據離本身很近,那麼會有助於提升數據計算的效率。要處理的數據量越大,則效率的提高越明顯。由於這會下降整個系統的網絡擁塞,並提升分佈式系統總體的吞吐量。HDFS提供了相應的API,能夠將application自己移向數據存儲的節點。架構
HDFS採用master/slave架構,集羣中master的角色由NameNode扮演,slave的角色由DataNode扮演。
一個HDFS集羣中只包含一個NameNode節點,它負責管理整個文件系統的命名空間(namespace),並對訪問文件的client進行權限控制。此外,一個集羣包含有多個DataNode節點,一般集羣中的每一個服務器上都運行一個DataNode程序,DataNode負責管理它所在服務器節點的存儲空間。
HDFS對外暴露一個統一的文件系統命名空間並容許用戶數據存儲在文件中。在HDFS內部,一個文件會被拆分紅一個或多個block,這些block會被存儲到一組DataNodes中。
NameNode會負責執行文件系統的一系列操做,如open、close、對文件或目錄rename等。此外,NameNode還會負責在文件block和DataNode間創建mapping關係。
DataNode會負責對從client發起的文件系統讀寫請求提供服務。DataNode還會負責文件block的建立和刪除,並根據NameNode的指示建立文件block的多副本。
NameNode和DataNode均可以運行在廉價的硬件設備上,這些硬件設備一般會安裝Linux操做系統。HDFS是由Java語言開發的,因此任何支持Java的平臺都可以運行NameNode和DataNode程序。一個HDFS集羣典型的部署方式是用一臺專門的服務器部署NameNode,再在集羣中的其它服務器各運行一個DataNode。也能夠在一個服務器上部署多個DataNode,但這種部署方式並不常見。
整個集羣只有一個NameNode節點大大簡化了HDFS系統的架構——NameNode負責管理整個文件系統的元數據(metadata)。在這種架構下,用戶數據只由DataNode處理,永遠不會流經NameNode節點。併發
HDFS支持傳統層級結構的文件組織方式(相似Linux文件結構),用戶能夠建立目錄結構並用於存儲文件。用戶能夠在HDFS建立、刪除文件,將文件從一個目錄轉移到另外一個目錄,對文件重命名等,此外還支持文件權限控制。HDFS不支持hard link和soft link,但在將來不排除實現這個特性的可能。
NameNode負責維護整個文件系統的namespace,任何對文件系統namespace的修改都會被記錄到NameNode中。例如,application能夠指定一個文件在HDFS中存儲的副本數量,這個副本數量叫作這個文件的副本因子(replication factor),這個信息就是存儲在NameNode中。app
HDFS的設計目標是可以跨集羣中多個服務器可靠存儲很是大的文件,它將文件切分紅一系列的block進行存儲。爲了實現容錯性,每一個文件block都會存儲多個副本。每一個文件的block大小和其副本因子都是能夠配置的,一個文件的全部block大小都相同(除了最後一個block)。application能夠指定文件副本的數量,副本因子既能夠在文件建立時指定,也能夠在文件建立好後修改。HDFS中的文件都只能被寫一次(除了appends和truncates),而且在任什麼時候刻都只能有一個writer在執行寫操做(不支持併發寫操做)。
NameNode負責管理全部的文件block replication,它會週期的收到集羣中的DataNode發送的心跳和Block report。收到心跳意味着DataNode工做正常,Blockreport則包含DataNode上全部block的列表。分佈式
在NameNode服務器上有一個普通文件叫作FsImage,它存儲了整個文件系統的namespace,包括blocks和文件的mapping,文件系統的其它屬性等也都被存儲在這個文件中。NameNode會將FsImage文件內容保存到內存中,以提升文件系統元數據的效率。雖然這樣讀取FsImage中的元數據會很高效,但若是每次文件系統元數據被修改就直接修改FsImage文件中的內容,效率會很是的差。
因此NameNode引入了Editlog機制,它利用一個被稱做Editlog的事務日誌持久化記錄文件系統元數據的每次變化。例如,當在HDFS中建立一個新的文件時,NameNode將會在EditLog中insert一條新的記錄標識此次變化。一樣的,修改一個文件的副本因子也會向Editlog中insert一條新的記錄。NameNode會藉助它所在服務器上操做系統的文件系統存儲Editlog,Editlog只是增量的記錄每一個對文件系統元數據的操做。
Editlog中存儲的只是對文件系統元數據修改的操做,這些操做的結果總歸要反應到FsImage中的數據變化才行。因此每隔一段時間,NameNode會結合Editlog和當前的FsImage生成一個新版本的FsImage,新版本的FsImage中包含了Editlog中記錄的操做對文件系統對元數據的修改。新版本的FsImage生成好以後,Editlog記錄的操做日誌就能夠被刪除了。這個過程被稱做checkpoint。
checkpoint的執行能夠經過配置指定。dfs.namenode.checkpoint.period配置指定通過多長時間執行一次checkpoint,單位時間是秒。dfs.namenode.checkpoint.txns配置執行通過多少次事務操做觸發一次checkpoint。若是同時配置了這兩個選項,那麼哪一個先知足條件,哪一個就會觸發checkpoint。
在NameNode剛啓動或者checkpoint期間,NameNode會執行從磁盤將FsImage文件的內容刷新到內存的操做。
DataNode並不知道文件的概念,在它眼中只有block,它存儲的只是一個一個block數據。oop
HDFS的主要目標就是即便在有硬件錯誤的狀況下,也要可以可靠的存儲數據。可能發生的錯誤主要有三種,分別是NameNode服務器出錯,DataNode服務器出錯,網絡異常引起腦裂。
每一個DataNode都會週期性的向NameNode發送心跳message。若是網絡異常致使整個網絡被割裂,將會致使有一部分DataNode與NameNode的鏈接斷開。NameNode能夠根據心跳message判斷與DataNode的鏈接情況,若是一段時間內都沒有收到DataNode的心跳message,那麼NameNode會把那個DataNode標記爲dead,而且再也不向被標記爲dead的DataNodes轉發任何IO請求。被標記爲dead的DataNodes上的全部數據對整個HDFS系統都是不可用的。
DataNode死亡可能會致使某些文件block的副本數量低於副本因子指定的副本數量,這就須要從新增長這些文件block的副本數量,使其恢復到原來指定的值。NameNode會持續跟蹤這些須要增長副本數量的文件block併爲其增長新的副本。
有下面幾種狀況,NameNode會爲文件block增長新的副本:
a. DataNode節點故障,致使在其上的全部文件block副本均不可用
b. DataNode服務器上的磁盤故障,致使該磁盤上的全部文件block所有不可用
c. 從新對一個文件的副本因子進行設置使其增大,表示須要更多的副本
將一個DataNode標記爲dead是很謹慎的,默認狀況下是NameNode在10分鐘內收不到一個DataNode的心跳message,纔會將這個DataNode標記爲dead。固然你能夠將這個時間設置的更短。
HDFS能夠對數據的存儲位置作調整使數據在整個集羣中的存儲「重平衡」,即將數據從一個DataNode節點移動到另外一個DataNode節點。但目前「重平衡」機制尚未被實現。
從DataNode獲取的數據有多是錯誤的,這種數據錯誤可能由存儲硬件、網絡錯誤和軟件bug等多種緣由致使。HDFS客戶端實現了對文件內容的checksum檢查機制。當客戶端在HDFS上建立一個文件時,它會對這個文件的每一個block都計算一個checksum,並將這些checksums分別保存在HDFS上的隱藏文件中。當客戶端接收文件內容時,它會驗證從DataNode發來的數據是否和存儲在隱藏文件中的checksum值匹配。若是不匹配,客戶端將選擇從另外一個DataNode節點獲取那個block的副本。
FsImage和EditLog是HDFS系統的核心數據結構,若是它們出現一點問題,那麼將會致使整個HDFS實例不可用。爲了防止FsImage和EditLog數據被破壞,能夠配置NameNode存儲FsImage和EditLog兩種數據的多份copy,任何FsImage和EditLog的修改操做都會觸發對全部copy的同步更新。這種同步更新所有copy的機制會致使NameNode的TPS降低,可是元數據的TPS降低是能夠接受的,由於HDFS應用通常都是讀多寫少,並不會對元數據作頻繁的修改。
另外一種加強NameNode的抗挫機制是增長多個NameNode節點,這點會在後面在討論。
能夠在某個時刻存儲HDFS整個系統的一個copy,這種機制能夠支持在HDFS數據被破壞時rollback到以前的一個good point。
一般狀況下,HDFS中的一個文件block大小爲128MB。HDFS中的一個文件會被切割成多個128MB的block存儲。若是條件容許,一個文件的每一個block會被分散到不一樣的DataNode節點存儲。
客戶端向HDFS寫入數據,假設副本因子是3。首先客戶端會先訪問NameNode節點,NameNode會根據replication target choosing algorithm從全部DataNodes中選出3個,並向客戶端返回由這3個DataNode組成的列表。被選中的這3個DataNode將用來存儲block的3個副本。
接着,客戶端會向第一個DataNode寫入數據,寫入數據的方式是分批(portion)進行的,寫完block的一批(一部分)數據後,再寫下一批數據。第一個DataNode節點接收到一批數據後,會將接收到的數據保存在本身本地的存儲,而後將數據轉發給列表中第二個DataNode節點。第二個DataNode節點接收到數據後,也會將數據保存到本身本地存儲,而後將數據轉發給列表中第三個DataNode節點。第三個節點接收到數據,將數據寫入到本身的本地存儲,至此一批數據算是寫入完成。
這三個DataNode構成了一條流水線,一個DataNode會從它前面的DataNode獲取數據,並將收到的數據轉發給它後面的DataNode。就這樣,數據沿着這條流水線從第一個DataNode轉發到最後一個DataNode。
詳細瞭解HDFS寫入數據流程,能夠參考這個漫畫。
HDFS的通訊協議是創建在TCP/IP協議之上的。NameNode不會主動發起鏈接請求,它只有對請求作出響應。