本文主要從兩個方面對hdfs進行闡述,第一就是hdfs的整個架構以及組成,第二就是hdfs文件的讀寫流程。html
1、HDFS概述node
標題中提到hdfs(Hadoop Distribute File System)是分佈式文件系統linux
分佈式文件系統 distributed file system 是指文件系統管理的物理存儲資源不必定直接連接在本地節點上,而是經過計算機網絡與節點相連,可以讓多機器上的多用戶分享文件和存儲空間。分佈式文件系統的設計基於客戶機/服務器模式sql
分佈式文件系統的特色:
一、分佈式文件系統能夠有效解決數據的存儲和管理難題
二、將固定於某個地點的某個文件系統,擴展到任意多個地點/多個文件系統
三、衆多的節點組成一個文件系統網絡
四、每一個節點能夠分佈在不一樣的地點,經過網絡進行節點間的通訊和數據傳輸
五、在使用分佈式文件系統時,無需關心數據是存儲在哪一個節點上、或者是從哪一個節點獲取的,只須要像使用本地文件系統同樣管理和存儲文件系統中的數據apache
Hadoop之(HDFS)是一種分佈式文件系統,設計用於在商用硬件上運行。 它與現有的分佈式文件系統有許多類似之處。 可是,與其餘分佈式文件系統的差別很大。
HDFS具備高度容錯能力,旨在部署在低成本硬件上。
HDFS提供對應用程序數據的高吞吐量訪問,適用於具備大型數據集的應用程序。
HDFS放寬了一些POSIX要求,以實現對文件系統數據的流式訪問安全
HDFS優點:服務器
一、可構建在廉價機器上,設備成本相對低
二、高容錯性,HDFS將數據自動保存多個副本,副本丟失後,自動恢復,防止數據丟失或損壞
三、適合批處理,HDFS適合一次寫入、屢次查詢(讀取)的狀況,適合在已有的數據進行屢次分析,穩定性好
四、適合存儲大文件,其中的大表示能夠存儲單個大文件,由於是分塊存儲,以及表示存儲大量的數據
HDFS劣勢:網絡
一、因爲提升吞吐量,下降實時性
二、因爲每一個文件都會在namenode中記錄元數據,若是存儲了大量的小文件,會對namenode形成很大的壓力
三、不合適小文件處理,在mapreduce的過程當中小文件的數量會形成map數量的增大,致使資源被佔用,並且速度慢。
四、不適合文件的修改,文件只能追加在文件的末尾,不支持任意位置修改,不支持多個寫入者操做
2、HDFS架構架構
hdfs架構圖以下圖所示:併發
HDFS具備主/從架構。HDFS集羣由單個NameNode,和多個datanode構成。
NameNode:管理文件系統命名空間的主服務器和管理客戶端對文件的訪問組成,如打開,關閉和重命名文件和目錄。負責管理文件目錄、文件和block的對應關係以及block和datanode的對應關係,維護目錄樹,接管用戶的請求。以下圖所示:
一、將文件的元數據保存在一個文件目錄樹中
二、在磁盤上保存爲:fsimage 和 edits
三、保存datanode的數據信息的文件,在系統啓動的時候讀入內存。
DataNode:(數據節點)管理鏈接到它們運行的節點的存儲,負責處理來自文件系統客戶端的讀寫請求。DataNodes還執行塊建立,刪除
Client:(客戶端)表明用戶經過與nameNode和datanode交互來訪問整個文件系統,HDFS對外開放文件命名空間並容許用戶數據以文件形式存儲。用戶經過客戶端(Client)與HDFS進行通信交互。
塊和複製:
咱們都知道linux操做系統中的磁盤的塊的大小默認是512,而hadoop2.x版本中的塊的大小默認爲128M,那爲何hdfs中的存儲塊要設計這麼大呢?
其目的是爲了減少尋址的開銷。只要塊足夠大,磁盤傳輸數據的時間一定會明顯大於這個塊的尋址時間。
那爲何要以塊的形式存儲文件,而不是整個文件呢?
一、由於一個文件能夠特別大,能夠大於有個磁盤的容量,因此以塊的形式存儲,能夠用來存儲不管大小怎樣的文件。
二、簡化存儲系統的設計。由於塊是固定的大小,計算磁盤的存儲能力就容易多了
三、以塊的形式存儲不須要所有存在一個磁盤上,能夠分佈在各個文件系統的磁盤上,有利於複製和容錯,數據本地化計算
塊和複本在hdfs架構中分佈以下圖所示:
既然namenode管理着文件系統的命名空間,維護着文件系統樹以及整顆樹內的全部文件和目錄,這些信息以文件的形式永遠的保存在本地磁盤上,分別問命名空間鏡像文件fsimage和編輯日誌文件Edits。datanode是文件的工做節點,根據須要存儲和檢索數據塊,而且按期的向namenode發送它們所存儲的塊的列表。那麼就知道namenode是多麼的重要,一旦那麼namenode掛了,那整個分佈式文件系統就不可使用了,因此對於namenode的容錯就顯得尤其重要了,hadoop爲此提供了兩種容錯機制:
容錯機制一:
就是經過對那些組成文件系統的元數據持久化,分別問命名空間鏡像文件fsimage(文件系統的目錄樹)和編輯日誌文件Edits(針對文件系統作的修改操做記錄)。磁盤上的映像FsImage就是一個Checkpoint,一個里程碑式的基準點、同步點,有了一個Checkpoint以後,NameNode在至關長的時間內只是對內存中的目錄映像操做,同時也對磁盤上的Edits操做,直到關機。下次開機的時候,NameNode要從磁盤上裝載目錄映像FSImage,那其實就是老的Checkpoint,也許就是上次開機後所保存的映像,而自從上次開機後直到關機爲止對於文件系統的全部改變都記錄在Edits文件中;將記錄在Edits中的操做重演於上一次的映像,就獲得這一次的新的映像,將其寫回磁盤就是新的Checkpoint(也就是fsImage)。可是這樣有很大一個缺點,若是Edits很大呢,開機後生成原始映像的過程也會很長,因此對其進行改進:每當 Edits長到必定程度,或者每隔必定的時間,就作一次Checkpoint,可是這樣就會給namenode形成很大的負荷,會影響系統的性能。因而就有了SecondaryNameNode的須要,這至關於NameNode的助理,專替NameNode作Checkpoint。固然,SecondaryNameNode的負載相比之下是偏輕的。因此若是爲NameNode配上了熱備份,就可讓熱備份兼職,而無須再有專職的SecondaryNameNode。因此架構圖以下圖所示:
SecondaryNameNode工做原理圖:
SecondaryNameNode主要負責下載NameNode中的fsImage文件和Edits文件,併合並生成新的fsImage文件,並推送給NameNode,工做原理以下:
一、secondarynamenode請求主namenode中止使用edits文件,暫時將新的寫操做記錄到一個新的文件中;
二、secondarynamenode從主namenode獲取fsimage和edits文件(經過http get)
三、secondarynamenode將fsimage文件載入內存,逐一執行edits文件中的操做,建立新的fsimage文件。
四、secondarynamenode將新的fsimage文件發送回主namenode(使用http post).
五、namenode用從secondarynamenode接收的fsimage文件替換舊的fsimage文件;用步驟1所產生的edits文件替換舊的edits文件。同時,還更新fstime文件來記錄檢查點執行時間。
六、最終,主namenode擁有最新的fsimage文件和一個更小的edits文件。當namenode處在安全模式時,管理員也可調用hadoop dfsadmin –saveNameSpace命令來建立檢查點。
從上面的過程當中咱們清晰的看到secondarynamenode和主namenode擁有相近內存需求的緣由(由於secondarynamenode也把fsimage文件載入內存)。所以,在大型集羣中,secondarynamenode須要運行在一臺專用機器上。
建立檢查點的觸發條件受兩個配置參數控制。一般狀況下,secondarynamenode每隔一小時(有fs.checkpoint.period屬性設置)建立檢查點;此外,當編輯日誌的大小達到64MB(有fs.checkpoint.size屬性設置)時,也會建立檢查點。系統每隔五分鐘檢查一次編輯日誌的大小。
容錯機制二:
高可用方案(詳情見:hadoop高可用安裝和原理詳解)
3、HDFS讀數據流程
HDFS讀數據流程以下圖所示:
一、客戶端經過FileSystem對象(DistributedFileSystem)的open()方法來打開但願讀取的文件。
二、DistributedFileSystem經過遠程調用(RPC)來調用namenode,獲取到每一個文件的起止位置。對於每個塊,namenode返回該塊副本的datanode。這些datanode會根據它們與客戶端的距離(集羣的網絡拓撲結構)排序,若是客戶端自己就是其中的一個datanode,那麼就會在該datanode上讀取數據。DistributedFileSystem遠程調用後返回一個FSDataInputStream(支持文件定位的輸入流)對象給客戶端以便於讀取數據,而後FSDataInputStream封裝一個DFSInputStream對象。該對象管理datanode和namenode的IO。
三、客戶端對這個輸入流調用read()方法,存儲着文件起始幾個塊的datanode地址的DFSInputStream隨即鏈接距離最近的文件中第一個塊所在的datanode,經過數據流反覆調用read()方法,能夠將數據從datanode傳送到客戶端。當讀完這個塊時,DFSInputStream關閉與該datanode的鏈接,而後尋址下一個位置最佳的datanode。
客戶端從流中讀取數據時,塊是按照打開DFSInputStream與datanode新建鏈接的順序讀取的。它也須要詢問namenode來檢索下一批所需塊的datanode的位置。一旦客戶端完成讀取,就對FSDataInputStream調用close()方法。
注意:在讀取數據的時候,若是DFSInputStream在與datanode通信時遇到錯誤,它便會嘗試從這個塊的另一個臨近datanode讀取數據。他也會記住那個故障datanode,以保證之後不會反覆讀取該節點上後續的塊。DFSInputStream也會經過校驗和確認從datanode發送來的數據是否完整。若是發現一個損壞的塊, DFSInputStream就會在試圖從其餘datanode讀取一個塊的複本以前通知namenode。
總結:在這個設計中,namenode會告知客戶端每一個塊中最佳的datanode,並讓客戶端直接聯繫該datanode且檢索數據。因爲數據流分散在該集羣中的全部datanode,因此這種設計會使HDFS可擴展到大量的併發客戶端。同時,namenode僅須要響應位置的請求(這些信息存儲在內存中,很是高效),而無需響應數據請求,不然隨着客戶端數量的增加,namenode很快會成爲一個瓶頸。
4、HDFS寫數據流程
HDFS寫數據流程圖以下圖所示:
一、首先客戶端經過DistributedFileSystem上的create()方法指明一個預建立的文件的文件名
二、DistributedFileSystem再經過RPC調用向NameNode申請建立一個新文件(這時該文件尚未分配相應的block)。namenode檢查是否有同名文件存在以及用戶是否有相應的建立權限,若是檢查經過,namenode會爲該文件建立一個新的記錄,不然的話文件建立失敗,客戶端獲得一個IOException異常。DistributedFileSystem返回一個FSDataOutputStream以供客戶端寫入數據,與FSDataInputStream相似,FSDataOutputStream封裝了一個DFSOutputStream用於處理namenode與datanode之間的通訊。
三、當客戶端開始寫數據時(,DFSOutputStream把寫入的數據分紅包(packet), 放入一箇中間隊列——數據隊列(data queue)中去。DataStreamer從數據隊列中取數據,同時向namenode申請一個新的block來存放它已經取得的數據。namenode選擇一系列合適的datanode(個數由文件的replica數決定)構成一個管道線(pipeline),這裏咱們假設replica爲3,因此管道線中就有三個datanode。
四、DataSteamer把數據流式的寫入到管道線中的第一個datanode中,第一個datanode再把接收到的數據轉到第二個datanode中,以此類推。
五、DFSOutputStream同時也維護着另外一箇中間隊列——確認隊列(ack queue),確認隊列中的包只有在獲得管道線中全部的datanode的確認之後纔會被移出確認隊列
若是某個datanode在寫數據的時候當掉了,下面這些對用戶透明的步驟會被執行:
管道線關閉,全部確認隊列上的數據會被挪到數據隊列的首部從新發送,這樣能夠確保管道線中當掉的datanode下流的datanode不會由於當掉的datanode而丟失數據包。
在還在正常運行的datanode上的當前block上作一個標誌,這樣噹噹掉的datanode從新啓動之後namenode就會知道該datanode上哪一個block是剛纔當機時殘留下的局部損壞block,從而能夠把它刪掉。
已經當掉的datanode從管道線中被移除,未寫完的block的其餘數據繼續被寫入到其餘兩個還在正常運行的datanode中去,namenode知道這個block還處在under-replicated狀態(也即備份數不足的狀態)下,而後他會安排一個新的replica從而達到要求的備份數,後續的block寫入方法同前面正常時候同樣。有可能管道線中的多個datanode當掉(雖然不太常常發生),但只要dfs.replication.min(默認爲1)個replica被建立,咱們就認爲該建立成功了。剩餘的replica會在之後異步建立以達到指定的replica數。
六、當客戶端完成寫數據後,它會調用close()方法。這個操做會沖洗(flush)全部剩下的package到pipeline中。
七、等待這些package確認成功,而後通知namenode寫入文件成功。這時候namenode就知道該文件由哪些block組成(由於DataStreamer向namenode請求分配新block,namenode固然會知道它分配過哪些blcok給給定文件),它會等待最少的replica數被建立,而後成功返回。
注意:hdfs在寫入的過程當中,有一點與hdfs讀取的時候很是類似,就是:DataStreamer在寫入數據的時候,每寫完一個datanode的數據塊,都會從新向nameNode申請合適的datanode列表。這是爲了保證系統中datanode數據存儲的均衡性。
hdfs寫入過程當中,datanode管線的確認應答包並非每寫完一個datanode,就返回一個確認應答,而是一直寫入,直到最後一個datanode寫入完畢後,統一返回應答包。若是中間的一個datanode出現故障,那麼返回的應答就是前面無缺的datanode確認應答,和故障datanode的故障異常。這樣咱們也就能夠理解,在寫入數據的過程當中,爲何數據包的校驗是在最後一個datanode完成。
更多hadoop生態文章見: hadoop生態系列
參考:
《Hadoop權威指南 大數據的存儲與分析 第四版》
https://hadoop.apache.org/docs/r2.7.7/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html