1. 簡介
HDFS是一個高容錯和可部署在廉價機器上的系統(tǒng)。HDFS提供高吞吐數(shù)據(jù)能力適合處理大量數(shù)據(jù)。HDFS松散了一些需求使得支持流式傳輸。HDFS原本是為Apache Butch的搜索引擎設計的,現(xiàn)在是Apache Hadoop項目的子項目。
HDFS以流式數(shù)據(jù)訪問模式(一次寫入、多次讀取)來存儲超大文件,運行于商用硬件集群上。
2. HDFS設計目標
- 硬件失效:HDFS實例可能包含上百成千個服務器,每個節(jié)點存儲著文件系統(tǒng)的部分數(shù)據(jù)。事實是集群有大量的節(jié)點,而每個節(jié)點都存在一定的概率失效也就意味著HDFS的一些組成部分經(jīng)常失效。因此,檢測錯誤、快速和自動恢復是HDFS的核心架構。
- 流式訪問:應用運行在HDFS需要允許流式訪問它的數(shù)據(jù)集。HDFS是被設計用于批量處理而非用戶交互。設計的重點是高吞吐量訪問而不是低延遲數(shù)據(jù)訪問。
- 大數(shù)據(jù)集:HDFS是用來支持大文件。它應該提供高帶寬和可擴展到上百節(jié)點在一個集群中。它應該支持在一個實例中有以千萬計的文件數(shù)。
- 簡單一致性模型:HDFS應用需要一個一次寫入多次讀取的文件訪問模型。一個文件一旦創(chuàng)建,寫入和關系都不需要改變。支持在文件的末端進行追加數(shù)據(jù)而不支持在文件的任意位置進行修改。
- 移動計算比移動數(shù)據(jù)更劃算:如果應用的計算在它要操作的數(shù)據(jù)附近執(zhí)行那就會更高效。尤其是數(shù)據(jù)集非常大的時候。這將最大限度地減少網(wǎng)絡擁堵和提高系統(tǒng)的吞吐量。
- 輕便的跨異構的軟硬件平臺:DFS被設計成可輕便從一個平臺跨到另一個平臺。
3. HDFS不擅長的事情
- 低時間延遲的數(shù)據(jù)訪問:要求低時間延遲數(shù)據(jù)訪問的應用,例如幾十毫秒范圍,不適合在HDFS上運行。目前,對于低延遲的訪問需求,HBase是更好的選擇。
- 大量的小文件:由于namenode將文件系統(tǒng)的元數(shù)據(jù)存儲在內(nèi)存中,因此該文件系統(tǒng)所能存儲的文件總數(shù)受限于namenode的內(nèi)存容量。
- 多用戶寫入,任意修改文件:他不支持具有多個寫入者的操作,也不支持在文件的任意位置進行修改。
4. HDFS的概念
- 數(shù)據(jù)塊:HDFS中的塊的大小默認設置為64MB,但是很多情況下HDFS使用128MB的塊設置。HDFS中小于一個塊大小的文件不會占據(jù)整個塊的空間。HDFS的塊設置的這么大(比磁盤的塊大的多)的目的是最小化尋址開銷。但是也不宜太大,因為MapReduce中的Map任務通常一次只處理一個塊中的數(shù)據(jù),因此如果任務數(shù)太少(少于集群中的節(jié)點數(shù)量),作業(yè)的運行速度就會比較慢。
5. NameNode and DataNodes
HDFS具有主/從架構。
- NameNode的工作: 一個HDFS集群包含一個NameNode,是一個主服務器,它用于管理文件系統(tǒng)名稱空間并管理客戶端對文件的訪問。NameNode執(zhí)行文件系統(tǒng)命名空間操作,如打開,關閉和重命名文件和目錄。 它還確定塊到DataNode的映射。 NameNode是所有HDFS元數(shù)據(jù)的仲裁者和存儲庫。 該系統(tǒng)的設計方式是用戶數(shù)據(jù)永遠不會流經(jīng)NameNode。NameNode控制著關于blocks復制的所有決定。它周期性地接收集群中DataNode發(fā)送的心跳和塊報告。收到心跳意味著DataNode在正常地運行著。一個塊報告包含著DataNode上所有塊信息的集合。
- DataNode的工作:通常是群集中的每個節(jié)點一個DataNode,用于管理連接到它們所運行的節(jié)點的存儲。 HDFS公開文件系統(tǒng)名稱空間并允許用戶數(shù)據(jù)存儲在文件中。 在內(nèi)部,文件被分成一個或多個塊,這些塊存儲在一組DataNode中。 DataNode負責提供來自文件系統(tǒng)客戶端的讀取和寫入請求。 DataNode還根據(jù)來自NameNode的指令執(zhí)行數(shù)據(jù)塊創(chuàng)建,刪除和復制。
- namenode的容錯:(1)第一種機制是備份那些組成文件系統(tǒng)元數(shù)據(jù)持久狀態(tài)的文件。這些寫操作是實時同步的,是原子操作。(2)另一種可信的辦法是運行一個輔助namenode,但它不能被用作namenode。這個輔助namenode的重要作用是定期通過編輯日志合并命名空間鏡像,以防止編輯日志過大。但是,輔助namenode保存的狀態(tài)總是滯后于主節(jié)點,所以在主節(jié)點全部失效時,難免會丟失部分數(shù)據(jù)。
- namenode的高可用性:通過配置了一對活動-備用(active-standby)namenode。當活動namenode失效,備用namenode就會接管他的任務并開始服務于來自客戶端的請求,不會有任何明顯中斷。
6. 副本選址策略
namenode如何選擇在那個datanode存儲副本,這里需要對可靠性、寫入帶寬和讀取帶寬進行權衡。
副本的選址對HDFS的可靠性和性能是起到關鍵作用的。機架感知副本配置策略的目的是提高可靠性、可用性和網(wǎng)絡帶寬的利用率。運行在集群計算機的大型HDFS實例一般是分布在許多機架上。兩個不同機架上的節(jié)點的通訊必須經(jīng)過交換機。在大多數(shù)情況下,同一個機架上的不同機器之間的網(wǎng)絡帶寬要優(yōu)于不同機架上的機器的。
通常情況下,當復制因子為3時,HDFS的副本放置策略是將一個副本放在本機架的一個節(jié)點上,將另一個副本放在本機架的另一個節(jié)點,最后一個副本放在不同機架的不同節(jié)點上。該策略減少機架內(nèi)部的傳輸以提高寫的性能。這個策略提高了寫性能而不影響數(shù)據(jù)可靠性和讀性能。為了最大限度地減少全局帶寬消耗和讀取延遲,HDFS試圖讓讀取者的讀取需求離副本最近。
7. 安全模式介紹
在啟動時,NameNode進入一個特殊的狀態(tài)稱之為安全模式。當NameNode進入安全模式之后數(shù)據(jù)塊的復制將不會發(fā)生。NameNode接收來自DataNode的心跳和數(shù)據(jù)塊報告。數(shù)據(jù)塊報告包含正在運行的DataNode上的數(shù)據(jù)塊信息集合。每個塊都指定了最小副本數(shù)。一個數(shù)據(jù)塊如果被NameNode檢查確保它滿足最小副本數(shù),那么它被認為是安全的。
NameNode存儲著HDFS的命名空間。NmaeNode使用一個稱之為EditLog的事務日志持續(xù)地記錄發(fā)生在文件系統(tǒng)元數(shù)據(jù)的每一個改變。NameNode在它本地的系統(tǒng)中用一個文件來存儲EditLog。整個文件系統(tǒng)命名空間,包括blocks的映射關系和文件系統(tǒng)屬性,將儲存在一個叫FsImage的文件。FsImage也是儲存在NameNode所在的本地文件系統(tǒng)中。
NameNode在內(nèi)存中保存著整個文件系統(tǒng)命名空間的圖像和文件映射關系。當Namenode啟動時,它從硬盤中讀取Edits和FsImage,將所有Edits中的事務作用在內(nèi)存中的FsImage上,并將這個新版本的FsImage從內(nèi)存中保存到本地磁盤上,然后刪除舊的Edits,因為這個舊的Edits的事務都已經(jīng)作用在FsImage上了。這個過程稱為一個檢查點(checkpoint)。只有當NameNode啟動時會執(zhí)行一次。
8. 魯棒性介紹
HDFS的主要目標是在失效出現(xiàn)時保證儲存的數(shù)據(jù)的可靠性。通常有這三種失效,分別為NameNode失效,DataNode失效和網(wǎng)絡分裂(一種在系統(tǒng)的任何兩個組之間的所有網(wǎng)絡連接同時發(fā)生故障后所出現(xiàn)的情況)
- 心跳機制:每個節(jié)點周期性地發(fā)送心跳信息給NameNode。網(wǎng)絡分裂會導致一部分DataNode失去與NameNode的連接。NameNode通過心跳信息的丟失發(fā)現(xiàn)這個情況。NameNode將最近沒有心跳信息的DataNode標記為死亡并且不再轉發(fā)任何IO請求給他們。在已經(jīng)死亡的DataNode注冊的任何數(shù)據(jù)在HDFS將不能再使用。DataNode死亡會導致部分數(shù)據(jù)塊的復制因子小于指定的數(shù)目。NameNode時常地跟蹤數(shù)據(jù)塊是否需要被復制和當必要的時候啟動復制。
- FsImage和EditLog是HDFS架構的中心數(shù)據(jù)。這些數(shù)據(jù)的失效會引起HDFS實例失效。因為這個原因,NameNode可以配置用來維持FsImage和EditLog的多個副本。FsImage或EditLog的任何改變會引起每一份FsImage和EditLog同步更新。
9. HDFS讀寫文件流程
1. 讀文件流程:
客戶端將要讀取的文件路徑發(fā)送給namenode,namenode獲取文件的元信息(主要是block的存放位置信息)返回給客戶端,客戶端根據(jù)返回的信息找到相應datanode逐個獲取文件的block并在客戶端本地進行數(shù)據(jù)追加合并從而獲得整個文件。

詳細步驟:
1.初始化FileSystem,然后客戶端(client)用FileSystem的open()函數(shù)打開文件。
2.FileSystem用RPC調(diào)用元數(shù)據(jù)節(jié)點,得到文件的數(shù)據(jù)塊信息,對于每一個數(shù)據(jù)塊,元數(shù)據(jù)節(jié)點返回保存數(shù)據(jù)塊的數(shù)據(jù)節(jié)點的地址。
3.FileSystem返回FSDataInputStream給客戶端,用來讀取數(shù)據(jù),客戶端調(diào)用stream的read()函數(shù)開始讀取數(shù)據(jù)。
4.DFSInputStream連接保存此文件第一個數(shù)據(jù)塊的最近的數(shù)據(jù)節(jié)點,data從數(shù)據(jù)節(jié)點讀到客戶端(client)
5.當此數(shù)據(jù)塊讀取完畢時,DFSInputStream關閉和此數(shù)據(jù)節(jié)點的連接,然后連接此文件下一個數(shù)據(jù)塊的最近的數(shù)據(jù)節(jié)點。
6.當客戶端讀取完畢數(shù)據(jù)的時候,調(diào)用FSDataInputStream的close函數(shù)。
7.在讀取數(shù)據(jù)的過程中,如果客戶端在與數(shù)據(jù)節(jié)點通信出現(xiàn)錯誤,則嘗試連接包含此數(shù)據(jù)塊的下一個數(shù)據(jù)節(jié)點。
- 失敗的數(shù)據(jù)節(jié)點將被記錄,以后不再連接。
2. 寫文件流程:
客戶端要向HDFS寫數(shù)據(jù),首先要跟namenode通信以確認可以寫文件并獲得接收文件block的datanode,然后,客戶端按順序將文件逐個block傳遞給相應datanode,并由接收到block的datanode負責向其他datanode復制block的副本。
客戶端創(chuàng)建文件的請求其實并沒有立即發(fā)送給Namenode,事實上,在剛開始階段HDFS客戶端會先將文件數(shù)據(jù)緩存到本地的一個臨時文件。應用程序的寫操作被透明地重定向到這個臨時文件。當這個臨時文件累積的數(shù)據(jù)量超過一個數(shù)據(jù)塊的大小,客戶端才會聯(lián)系Namenode。

詳細步驟:
1.初始化FileSystem,客戶端調(diào)用create()來創(chuàng)建文件。
2.FileSystem用RPC調(diào)用元數(shù)據(jù)節(jié)點,在文件系統(tǒng)的命名空間中創(chuàng)建一個新的文件,元數(shù)據(jù)節(jié)點首先確定文件原來不存在,并且客戶端有創(chuàng)建文件的權限,然后創(chuàng)建新文件。
3.FileSystem返回DFSOutputStream,客戶端用于寫數(shù)據(jù),客戶端開始寫入數(shù)據(jù)。
4.DFSOutputStream將數(shù)據(jù)分成塊,寫入data queue。data queue由Data Streamer讀取,并通知元數(shù)據(jù)節(jié)點分配數(shù)據(jù)節(jié)點,用來存儲數(shù)據(jù)塊(每塊默認復制3塊)。分配的數(shù)據(jù)節(jié)點放在一個pipeline里。Data Streamer將數(shù)據(jù)塊寫入pipeline中的第一個數(shù)據(jù)節(jié)點。第一個數(shù)據(jù)節(jié)點將數(shù)據(jù)塊發(fā)送給第二個數(shù)據(jù)節(jié)點。第二個數(shù)據(jù)節(jié)點將數(shù)據(jù)發(fā)送給第三個數(shù)據(jù)節(jié)點。
5.DFSOutputStream為發(fā)出去的數(shù)據(jù)塊保存了ack queue,等待pipeline中的數(shù)據(jù)節(jié)點告知數(shù)據(jù)已經(jīng)寫入成功。
6.當客戶端結束寫入數(shù)據(jù),則調(diào)用stream的close函數(shù)。此操作將所有的數(shù)據(jù)塊寫入pipeline中的數(shù)據(jù)節(jié)點,并等待ack queue返回成功。最后通知元數(shù)據(jù)節(jié)點寫入完畢。
7.如果數(shù)據(jù)節(jié)點在寫入的過程中失敗,關閉pipeline,將ack queue中的數(shù)據(jù)塊放入data queue的開始,當前的數(shù)據(jù)塊在已經(jīng)寫入的數(shù)據(jù)節(jié)點中被元數(shù)據(jù)節(jié)點賦予新的標示,則錯誤節(jié)點重啟后能夠察覺其數(shù)據(jù)塊是過時的,會被刪除。失敗的數(shù)據(jù)節(jié)點從pipeline中移除,另外的數(shù)據(jù)塊則寫入pipeline中的另外兩個數(shù)據(jù)節(jié)點。元數(shù)據(jù)節(jié)點則被通知此數(shù)據(jù)塊是復制塊數(shù)不足,將來會再創(chuàng)建第三份備份。
8.如果在寫的過程中某個datanode發(fā)生錯誤,會采取以下幾步:
1)pipeline被關閉掉;
2)為了防止防止丟包ack quene里的packet會同步到data quene里;
3)把產(chǎn)生錯誤的datanode上當前在寫但未完成的block刪掉;
4)block剩下的部分被寫到剩下的兩個正常的datanode中;
5)namenode找到另外的datanode去創(chuàng)建這個塊的復制。當然,這些操作對客戶端來說是無感知的。