1. 程式人生 > >hadoop之hdfs架構詳解

hadoop之hdfs架構詳解

本文主要從兩個方面對hdfs進行闡述,第一就是hdfs的整個架構以及組成,第二就是hdfs檔案的讀寫流程。

一、HDFS概述

     標題中提到hdfs(Hadoop Distribute File System)是分散式檔案系統

     分散式檔案系統 distributed file system 是指檔案系統管理的物理儲存資源不一定直接連結在本地節點上,而是通過計算機網路與節點相連,可讓多機器上的多使用者分享檔案和儲存空間。分散式檔案系統的設計基於客戶機/伺服器模式

分散式檔案系統的特點:
1、分散式檔案系統可以有效解決資料的儲存和管理難題
2、將固定於某個地點的某個檔案系統,擴充套件到任意多個地點/多個檔案系統
3、眾多的節點組成一個檔案系統網路
4、每個節點可以分佈在不同的地點,通過網路進行節點間的通訊和資料傳輸
5、在使用分散式檔案系統時,無需關心資料是儲存在哪個節點上、或者是從哪個節點獲取的,只需要像使用本地檔案系統一樣管理和儲存檔案系統中的資料

Hadoop之(HDFS)是一種分散式檔案系統,設計用於在商用硬體上執行。 它與現有的分散式檔案系統有許多相似之處。 但是,與其他分散式檔案系統的差異很大。
HDFS具有高度容錯能力,旨在部署在低成本硬體上。
HDFS提供對應用程式資料的高吞吐量訪問,適用於具有大型資料集的應用程式。
HDFS放寬了一些POSIX要求,以實現對檔案系統資料的流式訪問

HDFS優勢:

1、可構建在廉價機器上,裝置成本相對低
2、高容錯性,HDFS將資料自動儲存多個副本,副本丟失後,自動恢復,防止資料丟失或損壞
3、適合批處理,HDFS適合一次寫入、多次查詢(讀取)的情況,適合在已有的資料進行多次分析,穩定性好
4、適合儲存大檔案,其中的大表示可以儲存單個大檔案,因為是分塊儲存,以及表示儲存大量的資料

HDFS劣勢:

1、由於提高吞吐量,降低實時性
2、由於每個檔案都會在namenode中記錄元資料,如果儲存了大量的小檔案,會對namenode造成很大的壓力
3、不合適小檔案處理,在mapreduce的過程中小檔案的數量會造成map數量的增大,導致資源被佔用,而且速度慢。 4、不適合檔案的修改,檔案只能追加在檔案的末尾,不支援任意位置修改,不支援多個寫入者操作

 

二、HDFS架構

hdfs架構圖如下圖所示:

 

 

HDFS具有主/從架構。HDFS叢集由單個NameNode,和多個datanode構成。

NameNode:管理檔案系統名稱空間的主伺服器和管理客戶端對檔案的訪問組成,如開啟,關閉和重新命名檔案和目錄。負責管理檔案目錄、檔案和block的對應關係以及block和datanode的對應關係,維護目錄樹,接管使用者的請求。如下圖所示:

 

 

1、將檔案的元資料儲存在一個檔案目錄樹中
2、在磁碟上儲存為:fsimage 和 edits
3、儲存datanode的資料資訊的檔案,在系統啟動的時候讀入記憶體。

DataNode:(資料節點)管理連線到它們執行的​​節點的儲存,負責處理來自檔案系統客戶端的讀寫請求。DataNodes還執行塊建立,刪除

Client:(客戶端)代表使用者通過與nameNode和datanode互動來訪問整個檔案系統,HDFS對外開放檔案名稱空間並允許使用者資料以檔案形式儲存。使用者通過客戶端(Client)與HDFS進行通訊互動。

塊和複製:
我們都知道linux作業系統中的磁碟的塊的大小預設是512,而hadoop2.x版本中的塊的大小預設為128M,那為什麼hdfs中的儲存塊要設計這麼大呢?
其目的是為了減小定址的開銷。只要塊足夠大,磁碟傳輸資料的時間必定會明顯大於這個塊的定址時間。

那為什麼要以塊的形式儲存檔案,而不是整個檔案呢?
1、因為一個檔案可以特別大,可以大於有個磁碟的容量,所以以塊的形式儲存,可以用來儲存無論大小怎樣的檔案。
2、簡化儲存系統的設計。因為塊是固定的大小,計算磁碟的儲存能力就容易多了
3、以塊的形式儲存不需要全部存在一個磁碟上,可以分佈在各個檔案系統的磁碟上,有利於複製和容錯,資料本地化計算

塊和複本在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,工作原理如下:

1、secondarynamenode請求主namenode停止使用edits檔案,暫時將新的寫操作記錄到一個新的檔案中;
2、secondarynamenode從主namenode獲取fsimage和edits檔案(通過http get)
3、secondarynamenode將fsimage檔案載入記憶體,逐一執行edits檔案中的操作,建立新的fsimage檔案。
4、secondarynamenode將新的fsimage檔案傳送回主namenode(使用http post).
5、namenode用從secondarynamenode接收的fsimage檔案替換舊的fsimage檔案;用步驟1所產生的edits檔案替換舊的edits檔案。同時,還更新fstime檔案來記錄檢查點執行時間。
6、最終,主namenode擁有最新的fsimage檔案和一個更小的edits檔案。當namenode處在安全模式時,管理員也可呼叫hadoop dfsadmin –saveNameSpace命令來建立檢查點。

       從上面的過程中我們清晰的看到secondarynamenode和主namenode擁有相近記憶體需求的原因(因為secondarynamenode也把fsimage檔案載入記憶體)。因此,在大型叢集中,secondarynamenode需要執行在一臺專用機器上。

      建立檢查點的觸發條件受兩個配置引數控制。通常情況下,secondarynamenode每隔一小時(有fs.checkpoint.period屬性設定)建立檢查點;此外,當編輯日誌的大小達到64MB(有fs.checkpoint.size屬性設定)時,也會建立檢查點。系統每隔五分鐘檢查一次編輯日誌的大小。

容錯機制二:

高可用方案(詳情見:hadoop高可用安裝和原理詳解)

 

三、HDFS讀資料流程

HDFS讀資料流程如下圖所示:

 

 

1、客戶端通過FileSystem物件(DistributedFileSystem)的open()方法來開啟希望讀取的檔案。

2、DistributedFileSystem通過遠端呼叫(RPC)來呼叫namenode,獲取到每個檔案的起止位置。對於每一個塊,namenode返回該塊副本的datanode。這些datanode會根據它們與客戶端的距離(叢集的網路拓撲結構)排序,如果客戶端本身就是其中的一個datanode,那麼就會在該datanode上讀取資料。DistributedFileSystem遠端呼叫後返回一個FSDataInputStream(支援檔案定位的輸入流)物件給客戶端以便於讀取資料,然後FSDataInputStream封裝一個DFSInputStream物件。該物件管理datanode和namenode的IO。

3、客戶端對這個輸入流呼叫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很快會成為一個瓶頸。

 

四、HDFS寫資料流程

HDFS寫資料流程圖如下圖所示:

 

 

1、首先客戶端通過DistributedFileSystem上的create()方法指明一個預建立的檔案的檔名

2、DistributedFileSystem再通過RPC呼叫向NameNode申請建立一個新檔案(這時該檔案還沒有分配相應的block)。namenode檢查是否有同名檔案存在以及使用者是否有相應的建立許可權,如果檢查通過,namenode會為該檔案建立一個新的記錄,否則的話檔案建立失敗,客戶端得到一個IOException異常。DistributedFileSystem返回一個FSDataOutputStream以供客戶端寫入資料,與FSDataInputStream類似,FSDataOutputStream封裝了一個DFSOutputStream用於處理namenode與datanode之間的通訊。

3、當客戶端開始寫資料時(,DFSOutputStream把寫入的資料分成包(packet), 放入一箇中間佇列——資料佇列(data queue)中去。DataStreamer從資料佇列中取資料,同時向namenode申請一個新的block來存放它已經取得的資料。namenode選擇一系列合適的datanode(個數由檔案的replica數決定)構成一個管道線(pipeline),這裡我們假設replica為3,所以管道線中就有三個datanode。

4、DataSteamer把資料流式的寫入到管道線中的第一個datanode中,第一個datanode再把接收到的資料轉到第二個datanode中,以此類推。

5、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數。

6、當客戶端完成寫資料後,它會呼叫close()方法。這個操作會沖洗(flush)所有剩下的package到pipeline中。

7、等待這些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.