深入分析HDFS原理及讀寫流程

一、架構體系

1.1、什麼是HDFS?

HDFS即Hadoop Distributed File System的簡稱,採用Master/Slave主從結構模型來管理數據。在設計上採用了分而治之的思想,將單服務器無法承受的大量的數據分佈在多臺服務器上。HDFS主要由Client、NameNode、DataNode,SecondaryNameNode這四部分組成。

1.2、組成HDFS的各模塊作用
1.2.1、Client

HDFS客戶端是在DFSClient類的基礎上實現的,提供了命令行接口、API接口、瀏覽器接口等面向用戶的接口,使用戶可以不考慮HDFS的實現細節,簡化操作。

客戶端在整個HDFS的作用可以進行如下總結:

  1. 上傳文件時按照Block塊大小進行文件的切分;
  2. 和NameNode交互,獲取文件位置信息;
  3. 和DataNode交互,讀取和寫入數據;
  4. 管理和訪問整個HDFS。
1.2.2、NameNode

NameNode在HDFS結構模型裏充當Master的就角色,因此一個HDFS集羣裏只會有一個active的NameNode節點。在集羣裏主要用來處理客戶端的讀寫請求,它主要負責管理命名空間(NameSpace)和文件Block映射信息。

nameSpace:

nameSpace維護着文件系統樹(FileSystem Tree)和文件樹上的所有文件及文件夾的元數據(metadata),並使用fsimage和editlog這兩個文件來管理這些信息。fsimage(空間鏡像文件),它是文件系統元數據的一個完整的永久檢查點,內部維護的是最近一次檢查點的文件系統樹和整棵樹內部的所有文件和目錄的元數據,如修改時間,訪問時間,訪問權限,副本數據,塊大小,文件的塊列表信息等等。editlog(編輯日誌文件),當HDFS系統發生打開、關閉、創建、刪除、重命名等操作產生的信息除了在保存在內存中外,還會持久化到編輯日誌文件。比如上傳一個文件後,日誌文件裏記錄的有這次事務的tx id,文件的inode id,數據塊的副本數,數據塊的id,數據塊大小,訪問時間,修改時間等。

文件Block映射信息:

作爲一個master,NameNode需要記錄每個文件的每個塊所在的數據節點的位置信息,也就是我們常說的元數據信息metaData。但是由於NameNode並不進行持久化存儲,因此NameNode需要管理Block到DataNode的映射信息。一般元數據主要是文件名—> 數據塊映射和數據塊 —> Datanode列表映射。

其中文件名 —> 數據塊映射保存在磁盤上進行持久化存儲,但是NameNode並不保存數據塊 —> DataNode列表映射,這份列表是通過心跳機制(heartbeat)建立起來的。NameNode執行文件系統的namespace操作,如打開、關閉、重命名文件和目錄的同時決定了文件數據塊到具體DataNode節點的映射。

HDFS心跳機制

由於HDFS是master/slave結構,其中master包括namenode和resourcemanager,slave包括datanode和nodemanager。在master啓動時會開啓一個IPC服務,然後等待slave連接。當slave啓動後,會主動以默認3秒一次的頻率鏈接IPC服務。當然這個時間是可以調整的,這個每隔一段時間連接一次的機制,就是心跳機制(默認的 heartbeat.recheck.interval 大小爲 5 分鐘,dfs.heartbeat.interval 默認的大小爲 3 秒)。slave通過心跳給master彙報自己信息,master通過心跳下達命令。具體來說就是:

  1. Namenode通過心跳得知DataNode狀態,Resourcemanager通過心跳得知nodemanager狀態

  2. 當master很長時間沒有收到slave信息時,就認爲slave掛掉了。

這個判斷掛掉的時間計算公式:2recheck+10heartbeat(Recheck的時間單位爲毫秒,heartbeat的時間單位爲秒 ),默認爲10分鐘30秒 。

舉例:如果 heartbeat.recheck.interval 設置爲 6000(毫秒), dfs.heartbeat.interval設置爲 5(秒),則總的超時時間爲 62 秒。

NameNode作用小結:

  1. 管理命名空間NameSpace;
  2. 管理Block映射信息;
  3. 配置副本策略;
  4. 處理客戶端的讀寫請求。
1.2.3、DataNode

DataNode在HDFS結構裏裏是Slave的角色,因此就像咱們的實際生活一樣,在整個HDFS集羣裏DataNode是有很多的。DataNode負責數據塊的存儲和讀取,數據塊存儲在DataNode節點所在的本地文件系統中(包括block和block meta),並且它會利用心跳機制定期向NameNode發送自己所存儲的block塊映射列表。

數據塊:

數據塊是指存儲在HDFS中的最小單元,默認會在多個DataNode節點存儲3份,每個數據塊默認128M。**這裏爲什麼是128M呢?**因爲考慮到了尋址時間是10ms,而傳輸速率爲100MB/s,所以爲了最小化尋址的開銷同時兼顧傳輸效率,選擇了128M,這樣尋址只佔用傳輸時間的1%。

DataNode作用小結:

  1. 存儲實際的Block數據塊

  2. 執行數據塊的讀/寫操作

1.2.4、SecondaryNameNode

SecondaryNameNode不是NameNode的熱備份,因爲當NameNode停止服務時,它不能很快的替換NameNode。它更像是咱們現實生活中的老闆NameNode的祕書,平時整理整理文檔,幫老闆分擔工作。它主要是用來輔助NameNode進行fsimage和editlog的合併工作,可以減少editlog的文件大小。這樣就可以節省NameNode的重啓時間,可以儘快的退出安全模式。

檢查點機制:

editlog和fsimage兩個文件的合併週期,被稱爲檢查點機制(checkpoint)
fsimage文件是文件系統元數據的持久化檢查點,不會在寫操作後馬上更新,因爲fsimage寫非常慢。
由於editlog不斷增長,在NameNode重啓時,會造成長時間NameNode處於安全模式,不可用狀態,是非常不符合Hadoop的設計初衷。所以要週期性合併editlog,但是這個工作由Namenode來完成,會佔用大量資源,這樣就出現了SecondaryNamenode,它可以進行image檢查點的處理工作。

具體步驟如下:

  1. SecondaryNamenode請求Namenode進行editlog的滾動也就是創建一個新的editlog,然後將新的編輯操作記錄到新生成的editlog文件;
  2. 通過http get方式,讀取NameNode上的fsimage和edits文件,到SecondaryNamenode上;
  3. 讀取fsimage到內存中,即加載fsimage到內存,然後執行edits中所有操作,並生成一個新的fsimage文件(fsimage.ckpt臨時文件),也就是這個檢查點被創建了;
  4. 通過http post方式,將fsimage.ckpt臨時文件傳送到Namenode;
  5. Namenode使用新的fsimage替換原來的fsimage文件(fsimage.ckpt重命名爲fsimage),並且用第一步創建的edits(editlog)替代原來的edits文件;然後更新fsimage文件的檢查點時間。

SecondaryNameNode作用小結:

  1. 輔助NameNode,分擔其部分工作

  2. 定期合併fsimage和fsedits,並推送給NameNode

  3. 在緊急情況下,可輔助恢復NameNode

二、數據讀寫

HDFS的數據讀寫都分爲很多步驟,想要輕鬆的掌握它,有個簡單的小技巧------從粗略到細緻。也就是我們先知道大致的流程,然後再把每一步進行細化,最終全部掌握。

開始掌握讀寫流程前,需要先了解3個基本概念:

  • block

    前面也提到過block塊,它是數據存儲的最大單位,在進行文件上傳前client會對文件進行分塊,分得的塊就是block,默認128M,這是在綜合考慮尋址時間和傳輸效率的的情況下得出的最佳大小。

  • packet

    packet是client向DataNode傳輸數據時候的基本單位,默認64KB。

  • chunk

    chunk是進行數據校驗的基本單位,默認512Byte,加上4Byte的校驗位,實際上chunk寫入packet的大小爲516Byte,常見於client向DataNode進行的數據校驗。

2.1、讀數據

HDFS讀數據
粗略流程(便於記憶):

  1. client向namenode請求block所在的datanode節點列表;
  2. client從最近位置逐個依次從datanode中讀取block信息;
  3. 整個通過io流讀取的過程需要校驗每個快信息;
  4. 讀取完成,關閉所有流。

細緻流程(便於理解):

  1. 首先調用FileSystem的open方法獲取一個DistributedFileSystem實例;

  2. 然後DistributedFileSystem實例通過RPC在NameNode裏獲得文件的第一批block的locations(可能是需要讀取文件的全部,也可能是一部分),同一個block會按照在DataNode的重複數返回多個locations;

  3. 返回的多個locations會按照Hadoop拓撲結構排序,按照就近原則來排序;

  4. 前面三步結束後會返回一個FSDataInputStream對象,通過調用read方法時,該對象會找出離客戶端最近的DataNode並與之建立連接;

  5. 數據通過io流從DataNode源源不斷地流向客戶端;

  6. 如果第一個block塊數據讀取完成,就會關閉指向第一個block塊的DataNode連接,接着讀取下一個block塊,直到把這一批的block塊數據讀取完成;

  7. 每讀取完一個block塊都會進行checksum驗證(校驗每個塊的信息通過偏移量和預寫值對比,寫的時候是校驗packet的信息),如果讀取 DataNode 時出現錯誤,客戶端會 通知 NameNode,然後再從下一個擁有該 block 拷貝的 DataNode 繼續讀;

  8. 如果第一批blocks讀取完成,且文件讀取還沒有結束,也就是文件還沒讀完。FSDataInputStream就會向NameNode獲取下一批blocks的locations,然後重複上面的步驟,直到所有blocks讀取完成,這時就會關閉所有的流。

2.2、寫數據

HDFS寫數據

粗略流程(便於記憶):

  1. client向NameNode發送寫文件請求;
  2. NameNode檢查文件,如果通過就返回輸出流對象;
  3. client切分文件並且把數據和NameNode返回的DataNode列表一起發送給最近的一個DataNode節點;
  4. DataNode寫完之後返回確認信息;
  5. 數據全部寫完,關閉輸入輸出流,併發送完成信號給NameNode。

細緻流程(便於理解):

  1. 客戶端使用Configuration類加載配置文件信息,然後調用FileSystem的get()方法,獲取一個分佈式文件系統對象DistributedFileSystem。然後通過調用這個對象的create方法,向NameNode發送寫文件請求;
  2. 客戶端通過RPC與NameNode進行通信,NameNode需要經過各種不同的檢查,比如命名空間裏該路徑文件是否存在,客戶端是否有相應權限。如果沒有通過,返回IOException,反之如果檢查通過,NameNode就會在命名空間下新建該文件(此時新文件大小爲0字節),並記錄元數據,返回一個FSDataOutputStream輸出流對象;
  3. FSDataOutputStream封裝了一個DFSDataOutputStream對象,由該對象負責處理datanode和namenode之間的通信。(DistributedFileSystem.create()會調用DFSClient.create()方法創建DFSOutputStream輸出流並構造一個HdfsDataOutputStream來包裝DFSOutputStream);
  4. 客戶端把數據按照block塊進行切分;
  5. 然後調用DFSOutputStream的create方法,開始執行寫入操作(FSDataOutputStream封裝了一個DFSDataOutputStream對象,由該對象負責處理datanode和namenode之間的通信),DFSOutputStream會把數據切成一個個小packet,然後排成隊列 dataQueue;
  6. DataStreamer(DFSOutputStream的內部線程類)會去處理dataQueue,它先問詢 NameNode 這個新的 block 最適合存儲的在哪幾個DataNode裏,比如重複數是3,那麼就找到3個最適合的 DataNode,把它們排成一個 pipeline。DataStreamer 把 packet 按隊列輸出到管道的第一個 DataNode 的內存中,然後第一個 DataNode又把 packet 輸出到第二個 DataNode 中,以此類推;
  7. 在DataStreamer將packet寫入pipeline時,同時也會將該packet存儲到另外一個由ResponseProcessor線程管理的緩存隊列ackqueue確認隊列中。ResponseProcessor線程會等待DataNode的確認響應。當收到所有的DataNode的確認信息後,該線程再將ackqueue裏的packet刪除;
  8. 如果寫入期間發生故障,會首先關閉pipeline,把ackqueue的所有packet都放回dataqueue的最前端,以確保故障節點後的節點不會漏掉任一packet。同時,會標識正常的DataNode,方便在故障節點恢復後,刪除錯誤的部分數據塊。然後從管線中刪除故障節點,基於新的DataNode構建一個新的管線;
  9. 在一個block塊大小的n個packet數據包寫完後,客戶端會調用FSDataOutputStream的close方法關閉寫入流,當然在調用close之前,DataNode會將內存中的數據寫入本地磁盤;
  10. 最後DataStreamer會繼續向NameNode請求下一個塊的DataNode列表,開始下一個塊的寫入。直到寫完整個文件的最後一個塊數據,然後客戶端通知 NameNode 把文件標示爲已完成,到這裏整個寫入過程就結束了。

三、優缺點

數據的讀寫是HDFS的核心和考察重點,下面在結合着總結一下優缺點:

優點:

  1. 簡單一致性模型:一次寫入,多次讀取,但要注意不能修改,只能追加;
  2. 高容錯,低成本:可以搭建在廉價的機器上,並且數據以多副本保存在不同的服務器上,某個副本丟失,也能通過別的副本進行恢復;
  3. 流式數據訪問:不是隨機讀寫;
  4. 適合大規模數據集:能夠進行批處理,支持橫向擴展,支持PB級數據和10k節點規模。

缺點:

  1. 延遲高:不支持低延遲數據訪問,做不到毫秒級存儲數據,但是適合高吞吐率(某一時間內寫入大量的數據)的場景;
  2. 不適合小文件:每條元數據佔用空間是一定的,因此大量小文件會佔用NameNode大量的內存來存儲文件、目錄和塊信息;
  3. 不支持併發寫入:一個文件只允許一個線程進行寫操作,不適合併發寫入;
  4. 不能修改和隨機讀寫:文件不允許修改,只支持追加,同時也不是隨機讀寫。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章