Hadoop核心之HDFS 架構設計

caixingyun發表於2017-02-14

來自:http://blog.csdn.net/suifeng3051/article/details/48548341

概述:HDFS即Hadoop Distributed File System分散式檔案系統,它的設計目標是把超大資料集儲存到分佈在網路中的多臺普通商用計算機上,並且能夠提供高可靠性和高吞吐量的服務。分散式檔案系統要比普通磁碟檔案系統複雜,因為它要引入網路程式設計,分散式檔案系統要容忍節點故障也是一個很大的挑戰。

設計前提和目標

  1. 專為儲存超大檔案而設計:hdfs應該能夠支援GB級別大小的檔案;它應該能夠提供很大的資料頻寬並且能夠在叢集中擴充到成百上千個節點;它的一個例項應該能夠支援千萬數量級別的檔案。
  2. 適用於流式的資料訪問:hdfs適用於批處理的情況而不是互動式處理;它的重點是保證高吞吐量而不是低延遲的使用者響應
  3. 容錯性:完善的冗餘備份機制
  4. 支援簡單的一致性模型:HDFS需要支援一次寫入多次讀取的模型,而且寫入過程檔案不會經常變化
  5. 移動計算優於移動資料:HDFS提供了使應用計算移動到離它最近資料位置的介面
  6. 相容各種硬體和軟體平臺

不適合的場景

  1. 大量小檔案:檔案的後設資料都儲存在NameNode記憶體中,大量小檔案會佔用大量記憶體。
  2. 低延遲資料訪問:hdfs是專門針對高資料吞吐量而設計的
  3. 多使用者寫入,任意修改檔案

hdfs架構設計

HDFS主要由3個元件構成,分別是NameNodeSecondaryNameNodeDataNode,HSFS是以master/slave模式執行的,其中NameNode、SecondaryNameNode 執行在master節點,DataNode執行slave節點。

資料塊

磁碟資料塊是磁碟讀寫的基本單位,與普通檔案系統類似,hdfs也會把檔案分塊來儲存。hdfs預設資料塊大小為64MB,磁碟塊一般為512B,hdfs塊為何如此之大呢?塊增大可以減少定址時間與檔案傳輸時間的比例,若定址時間為10ms,磁碟傳輸速率為100MB/s,那麼定址與傳輸比僅為1%。當然,磁碟塊太大也不好,因為一個MapReduce通常以一個塊作為輸入,塊過大會導致整體任務數量過小,降低作業處理速度。

資料塊是儲存在DataNode中的,為了能夠容錯資料塊是以多個副本的形式分佈在叢集中的,副本數量預設為3,後面會專門介紹資料塊的複製機制。

hdfs按塊儲存還有如下好處:

  1. 檔案可以任意大,也不用擔心單個結點磁碟容量小於檔案的情況
  2. 簡化了檔案子系統的設計,子系統只儲存檔案塊資料,而檔案後設資料則交由其它系統(NameNode)管理
  3. 有利於備份和提高系統可用性,因為可以以塊為單位進行備份,hdfs預設備份數量為3。
  4. 有利於負載均衡

NameNode

關於NameNode中的元資訊

當一個客戶端請求一個檔案或者儲存一個檔案時,它需要先知道具體到哪個DataNode上存取,獲得這些資訊後,客戶端再直接和這個DataNode進行互動,而這些資訊的維護者就是NameNode。

NameNode管理著檔案系統名稱空間,它維護這檔案系統樹及樹中的所有檔案和目錄。NameNode也負責維護所有這些檔案或目錄的開啟、關閉、移動、重新命名等操作。對於實際檔案資料的儲存與操作,都是由DataNode負責。當一個客戶端請求資料時,它僅僅是從NameNode中獲取檔案的元資訊,而具體的資料傳輸不需要經過NameNode,是由客戶端直接與相應的DataNode進行互動。

NameNode儲存元資訊的種類有:

  • 檔名目錄名及它們之間的層級關係
  • 檔案目錄的所有者及其許可權
  • 每個檔案塊的名及檔案有哪些塊組成

需要注意的是,NameNode元資訊並不包含每個塊的位置資訊,這些資訊會在NameNode啟動時從各個DataNode獲取並儲存在記憶體中,因為這些資訊會在系統啟動時由資料節點重建。把塊位置資訊放在記憶體中,在讀取資料時會減少查詢時間,增加讀取效率。NameNode也會實時通過心跳機制和DataNode進行互動,實時檢查檔案系統是否執行正常。不過NameNode元資訊會儲存各個塊的名稱及檔案由哪些塊組成。

一般來說,一條元資訊記錄會佔用200byte記憶體空間。假設塊大小為64MB,備份數量是3 ,那麼一個1GB大小的檔案將佔用16*3=48個檔案塊。如果現在有1000個1MB大小的檔案,則會佔用1000*3=3000個檔案塊(多個檔案不能放到一個塊中)。我們可以發現,如果檔案越小,儲存同等大小檔案所需要的元資訊就越多,所以,Hadoop更喜歡大檔案。

元資訊的持久化

在NameNode中存放元資訊的檔案是 fsimage。在系統執行期間所有對元資訊的操作都儲存在記憶體中並被持久化到另一個檔案edits中。並且edits檔案和fsimage檔案會被SecondaryNameNode週期性的合併(合併過程會在SecondaryNameNode中詳細介紹)。

其它問題

執行NameNode會佔用大量記憶體和I/O資源,一般NameNode不會儲存使用者資料或執行MapReduce任務。

為了簡化系統的設計,Hadoop只有一個NameNode,這也就導致了hadoop叢集的單點故障問題。因此,對NameNode節點的容錯尤其重要,hadoop提供瞭如下兩種機制來解決:

  • 將hadoop後設資料寫入到本地檔案系統的同時再實時同步到一個遠端掛載的網路檔案系統(NFS)。
  • 執行一個secondary NameNode,它的作用是與NameNode進行互動,定期通過編輯日誌檔案合併名稱空間映象,當NameNode發生故障時它會通過自己合併的名稱空間映象副本來恢復。需要注意的是secondaryNameNode儲存的狀態總是滯後於NameNode,所以這種方式難免會導致丟失部分資料(後面會詳細介紹)。

DataNode

DataNode是hdfs中的worker節點,它負責儲存資料塊,也負責為系統客戶端提供資料塊的讀寫服務,同時還會根據NameNode的指示來進行建立、刪除、和複製等操作。此外,它還會通過心跳定期向NameNode傳送所儲存檔案塊列表資訊。當對hdfs檔案系統進行讀寫時,NameNode告知客戶端每個資料駐留在哪個DataNode,客戶端直接與DataNode進行通訊,DataNode還會與其它DataNode通訊,複製這些塊以實現冗餘。

NameNode和DataNode架構圖 
這裡寫圖片描述

SecondaryNameNode

需要注意,SecondaryNameNode並不是NameNode的備份。我們從前面的介紹已經知道,所有HDFS檔案的元資訊都儲存在NameNode的記憶體中。在NameNode啟動時,它首先會載入fsimage到記憶體中,在系統執行期間,所有對NameNode的操作也都儲存在了記憶體中,同時為了防止資料丟失,這些操作又會不斷被持久化到本地edits檔案中。

Edits檔案存在的目的是為了提高系統的操作效率,NameNode在更新記憶體中的元資訊之前都會先將操作寫入edits檔案。在NameNode重啟的過程中,edits會和fsimage合併到一起,但是合併的過程會影響到Hadoop重啟的速度,SecondaryNameNode就是為了解決這個問題而誕生的。

SecondaryNameNode的角色就是定期的合併edits和fsimage檔案,我們來看一下合併的步驟:

  1. 合併之前告知NameNode把所有的操作寫到新的edites檔案並將其命名為edits.new。
  2. SecondaryNameNode從NameNode請求fsimage和edits檔案
  3. SecondaryNameNode把fsimage和edits檔案合併成新的fsimage檔案
  4. NameNode從SecondaryNameNode獲取合併好的新的fsimage並將舊的替換掉,並把edits用第一步建立的edits.new檔案替換掉
  5. 更新fstime檔案中的檢查點

最後再總結一下整個過程中涉及到NameNode中的相關檔案

  • fsimage :儲存的是上個檢查點的HDFS的元資訊
  • edits :儲存的是從上個檢查點開始發生的HDFS元資訊狀態改變資訊
  • fstime:儲存了最後一個檢查點的時間戳

資料備份

HDFS通過備份資料塊的形式來實現容錯,除了檔案的最後一個資料塊外,其它所有資料塊大小都是一樣的。資料塊的大小和備份因子都是可以配置的。NameNode負責各個資料塊的備份,DataNode會通過心跳的方式定期的向NameNode傳送自己節點上的Block 報告,這個報告中包含了DataNode節點上的所有資料塊的列表。

檔案副本的分佈位置直接影響著HDFS的可靠性和效能。一個大型的HDFS檔案系統一般都是需要跨很多機架的,不同機架之間的資料傳輸需要經過閘道器,並且,同一個機架中機器之間的頻寬要大於不同機架機器之間的頻寬。如果把所有的副本都放在不同的機架中,這樣既可以防止機架失敗導致資料塊不可用,又可以在讀資料時利用到多個機架的頻寬,並且也可以很容易的實現負載均衡。但是,如果是寫資料,各個資料塊需要同步到不同的機架,會影響到寫資料的效率。

而在Hadoop中,如果副本數量是3的情況下,Hadoop預設是這麼存放的,把第一個副本放到機架的一個節點上,另一個副本放到同一個機架的另一個節點上,把最後一個節點放到不同的機架上。這種策略減少了跨機架副本的個數提高了寫的效能,也能夠允許一個機架失敗的情況,算是一個很好的權衡。

關於副本的選擇,在讀的過程中,HDFS會選擇最近的一個副本給請求者。

關於安全模式,當 Hadoop的NameNode節點啟動時,會進入安全模式階段。在此階段,DataNode會向NameNode上傳它們資料塊的列表,讓 NameNode得到塊的位置資訊,並對每個檔案對應的資料塊副本進行統計。當最小副本條件滿足時,即一定比例的資料塊都達到最小副本數,系統就會退出安全模式,而這需要一定的延遲時間。當最小副本條件未達到要求時,就會對副本數不足的資料塊安排DataNode進行復制,直至達到最小副本數。而在安全模式下,系統會處於只讀狀態,NameNode不會處理任何塊的複製和刪除命令。

HDFS中的溝通協議

所有的HDFS中的溝通協議都是基於tcp/ip協議,一個客戶端通過指定的tcp埠與NameNode機器建立連線,並通過ClientProtocol協議與NameNode互動。而DataNode則通過DataNode Protocol協議與NameNode進行溝通。HDFS的RCP(遠端過程呼叫)對ClientProtocol和DataNode Protocol做了封裝。按照HDFS的設計,NameNode不會主動發起任何請求,只會被動接受來自客戶端或DataNode的請求。

可靠性保證

可以允許DataNode失敗。DataNode會定期(預設3秒)的向NameNode傳送心跳,若NameNode在指定時間間隔內沒有收到心跳,它就認為此節點已經失敗。此時,NameNode把失敗節點的資料(從另外的副本節點獲取)備份到另外一個健康的節點。這保證了叢集始終維持指定的副本數。

可以檢測到資料塊損壞。在讀取資料塊時,HDFS會對資料塊和儲存的校驗和檔案匹配,如果發現不匹配,NameNode同樣會重新備份損壞的資料塊。

hdfs檔案讀寫過程剖析

瞭解客戶端與NameNode和DataNode的互動過程十分重要,有助於加深我們對hdfs架構設計的理解。

hdfs檔案讀取過程

hdfs有一個FileSystem例項,客戶端通過呼叫這個例項的open()方法就可以開啟系統中希望讀取的檔案。hdfs通過rpc呼叫NameNode獲取檔案塊的位置資訊,對於檔案的每一個塊,NameNode會返回含有該塊副本的DataNode的節點地址,另外,客戶端還會根據網路拓撲來確定它與每一個DataNode的位置資訊,從離它最近的那個DataNode獲取資料塊的副本,最理想的情況是資料塊就儲存在客戶端所在的節點上。

hdfs會返回一個FSDataInputStream物件,FSDataInputStream類轉而封裝成DFSDataInputStream物件,這個物件管理著與DataNode和NameNode的I/O,具體過程是:

1. 客戶端發起讀請求
2. 客戶端與NameNode得到檔案的塊及位置資訊列表
3. 客戶端直接和DataNode互動讀取資料
4. 讀取完成關閉連線

這裡寫圖片描述

當FSDataInputStream與DataNode通訊時遇到錯誤,它會選取另一個較近的DataNode,併為出故障的DataNode做標記以免重複向其讀取資料。FSDataInputStream還會對讀取的資料塊進行校驗和確認,發現塊損壞時也會重新讀取並通知NameNode。

這樣設計的巧妙之處:

  1. 讓客戶端直接聯絡DataNode檢索資料,可以使hdfs擴充套件到大量的併發客戶端,因為資料流就是分散在叢集的每個節點上的,在執行MapReduce任務時,每個客戶端就是一個DataNode節點。
  2. NameNode僅需相應塊的位置資訊請求(位置資訊在記憶體中,速度極快),否則隨著客戶端的增加,NameNode會很快成為瓶頸。

關於hadoop與網路拓撲

在海量資料處理過程中,主要限制因素是節點之間的頻寬。衡量兩個節點之間的頻寬往往很難實現,在這裡hadoop採取了一個簡單的方法,它把網路拓撲看成是一棵樹,連個節點的距離=它們到最近共同祖先距離的總和,而樹的層次可以這麼劃分:

  • 同一節點中的程式
  • 同一機架上的不同節點
  • 同一資料中心不同機架
  • 不同資料中心的節點

若資料中心d1中一個機架r1中一個節點n1表示為d1/r1/n1,則:

 distance(d1/r1/n1,d1/r1/n1)=0;
 distance(d1/r1/n1,d1/r1/n2)=2;
 distance(d1/r1/n1,d1/r2/n3)=4;
 distance(d1/r1/n1,d2/r3/n4)=6;

hdfs檔案寫入過程

hdfs有一個DistributedFileSystem例項,客戶端通過呼叫這個例項的create()方法就可以建立檔案。DistributedFileSystem會傳送給NameNode一個RPC呼叫,在檔案系統的名稱空間建立一個新檔案,在建立檔案前NameNode會做一些檢查,如檔案是否存在,客戶端是否有建立許可權等,若檢查通過,NameNode會為建立檔案寫一條記錄到本地磁碟的EditLog,若不通過會向客戶端丟擲IOException。建立成功之後DistributedFileSystem會返回一個FSDataOutputStream物件,客戶端由此開始寫入資料。

同讀檔案過程一樣,FSDataOutputStream類轉而封裝成DFSDataOutputStream物件,這個物件管理著與DataNode和NameNode的I/O,具體過程是:

1. 客戶端在向NameNode請求之前先寫入檔案資料到本地檔案系統的一個臨時檔案
2. 待臨時檔案達到塊大小時開始向NameNode請求DataNode資訊
3. NameNode在檔案系統中建立檔案並返回給客戶端一個資料塊及其對應DataNode的地址列表(列表中包含副本存放的地址)
4. 客戶端通過上一步得到的資訊把建立臨時檔案塊flush到列表中的第一個DataNode
5. 當檔案關閉,NameNode會提交這次檔案建立,此時,檔案在檔案系統中可見

這裡寫圖片描述

上面第四步描述的flush過程實際處理過程比較負雜,現在單獨描述一下:

1. 首先,第一個DataNode是以資料包(資料包一般4KB)的形式從客戶端接收資料的,DataNode在把資料包寫入到本地磁碟的同時會向第二個DataNode(作為副本節點)傳送資料。
2. 在第二個DataNode把接收到的資料包寫入本地磁碟時會向第三個DataNode傳送資料包
3. 第三個DataNode開始向本地磁碟寫入資料包。此時,資料包以流水線的形式被寫入和備份到所有DataNode節點
4. 傳送管道中的每個DataNode節點在收到資料後都會向前面那個DataNode傳送一個ACK,最終,第一個DataNode會向客戶端發回一個ACK
5. 當客戶端收到資料塊的確認之後,資料塊被認為已經持久化到所有節點。然後,客戶端會向NameNode傳送一個確認
6. 如果管道中的任何一個DataNode失敗,管道會被關閉。資料將會繼續寫到剩餘的DataNode中。同時NameNode會被告知待備份狀態,NameNode會繼續備份資料到新的可用的節點
7. 資料塊都會通過計算校驗和來檢測資料的完整性,校驗和以隱藏檔案的形式被單獨存放在hdfs中,供讀取時進行完整性校驗

hdfs檔案刪除過程

hdfs檔案刪除過程一般需要如下幾步:

1. 一開始刪除檔案,NameNode只是重新命名被刪除的檔案到/trash目錄,因為重新命名操作只是元資訊的變動,所以整個過程非常快。在/trash中檔案會被保留一定間隔的時間(可配置,預設是6小時),在這期間,檔案可以很容易的恢復,恢復只需要將檔案從/trash移出即可。
2. 當指定的時間到達,NameNode將會把檔案從名稱空間中刪除
3. 標記刪除的檔案塊釋放空間,HDFS檔案系統顯示空間增加

相關文章