HDFS原理講解


簡介

本文是筆者在學習HDFS的時候的學習筆記整理, 將HDFS的核心功能的原理都整理在這里了。

【廣告】 如果你喜歡本博客,請點此查看本博客所有文章:http://www.cnblogs.com/xuanku/p/index.html

HDFS的基礎架構

見下圖, 核心角色: Client, NameNode, Secondary NameNode, DataNode

hdfs架構圖

  1. Client: 對用戶提供系列操作工具&API

  2. NameNode:

    1. 包含map<filename, list<block_id>>, 以及map<block_id, list<DataNode>>的數據結構
    2. 資源分配算法
  3. DataNode:

    1. 管理好自己的磁盤, 上報數據給NameNode

讀取過程

  1. Client向NameNode讀取數據分布式信息
  2. Client找到第一個數據塊離自己最近的DataNode
  3. 跟這個DataNode交互並獲取數據
  4. 讀完之后開始跟下一個數據塊離自己最近的DataNode交互
  5. 讀完之后close連接
  6. 如果讀取過程中讀取失敗, 將會依次讀取該數據塊下一個副本, 失敗的節點將被記錄, 不再連接

近的判斷標准(NetworkTopology.sortByDistance):

  1. 如果客戶端和某個Datanode在同一台機器上, 優先
  2. 如果客戶端和某個Datanode在同一個rack上, 次優先
  3. 否則隨機

詳情請參考下圖:

hdfs讀取數據流圖

參考:

  1. 讀取判斷遠近. http://blog.csdn.net/xhh198781/article/details/7256142

寫入過程

流程

  1. 客戶端通知NameNode創建目錄
  2. 客戶端開始寫數據, 先寫到本地, 然后定期分塊
  3. 要寫新塊的時候再跟NameNode打交道, 獲取到新塊的目標地址
  4. 同一個數據塊的不同副本是鏈式同步, 客戶端只跟第一個副本打交道
  5. 只有所有副本都寫入成功, 才開始下一個塊的寫操作
  6. 如果有一個寫失敗, 則:
    1. 失敗的DataNode會加一個標記, 根據這個標記, 這份不完全的數據回頭會被刪除
    2. 不再往失敗的DataNode上面寫, 其他兩個DataNode繼續寫
    3. 告訴NameNode這份數據副本數不足, NameNode回頭會異步的補上
    4. 如果副本數少於某個配置(比如1個), 整個寫入就算失敗

詳情參考下圖:

hdfs寫入數據流圖

副本分配策略

  1. 選擇一個本地節點

  2. 選擇一個本地機架節點

  3. 選擇一個遠程節點

  4. 隨機選擇一個節點

  5. 調整同步順序(以節約帶寬為目標)

參考: http://www.linuxidc.com/Linux/2012-01/50864.htm

如何感知機架位

通過NameNode的一個配置: topology.script.file.name 來控制的, 該配置的值對應一個腳本, 腳本輸入是一個IP/字符串, 輸出一個機架位名稱, 該名稱可以用"/xx/xx"的樹形結構來表示網絡拓撲。

如果沒有配置該值, 則代表所有機器一個機架位, 會增加機器網絡帶寬消耗。

參考: http://www.cnblogs.com/ggjucheng/archive/2013/01/03/2843015.html

HDFS Append文件邏輯

首先要說明兩個概念, block和replica, 在NN中叫block, 在DN中叫replica。

block有四種狀態:

  1. complete. block的長度和時間戳都不再變化, 並且至少有一個DN對應的rpelica是finalized的狀態。
  2. under_construction. 文件被create和append的時候, 該block就處於under_construction的狀態。該狀態下文件是可以被讀取的, 讀取的長度是保證在所有的DN上副本都能讀取到的長度;
  3. under_recovery. 如果一個文件的最后一個block在under_construction的狀態時, client異常掉線了, 那么需要有一段時間的lease過期和恢復釋放鎖和關閉文件的過程, 這段時間之內該block處於under_recovery的狀態.
  4. committed. 介於under_construction和complete之間的狀態。client收到所有DN寫成功的ARK, 但是NN還沒有收到任何一個DN報replica已經finalized的狀態。

replica狀態要復雜一些:

  1. Finalized. 寫完事兒之后的狀態。
  2. rbw(replica being written). 類似under_construction, 在創建和寫入過程中的replica。同under_construction, 也是可以被讀取的;
  3. rwr(rpelica waiting recovry). 異常之后, 等待lease過期的狀態;
  4. rur(replica under recovery). 異常之后, lease過期之后修復數據的狀態;
  5. Temporary. 類似rbw, 但是不是正常寫的狀態, 是比如集群間balance的狀態。跟rbw不同的是, 同步中的文件不可讀;

參考: http://yanbohappy.sinaapp.com/?p=175

SecondaryNameNode機制

  1. SecondaryNameNode不是說NameNode掛了的備用節點
  2. 他的主要功能只是定期合並日志, 防止日志文件變得過大
  3. 合並過后的鏡像文件在NameNode上也會保存一份

SecondaryNameNode工作過程:

  1. SecondaryNameNode向NameNode發起同步請求, 此時NameNode會將日志都寫到新的日志當中
  2. SecondaryNameNode向NameNode下載鏡像文件+日志文件
  3. SecondaryNameNode開始Merge這兩份文件並生成新的鏡像文件
  4. SecondaryNameNode向NameNode傳回新的鏡像文件
  5. NameNode文件將新的鏡像文件和日志文件替換成當前正在使用的文件

詳情請參考下圖:

SecondaryNameNode數據流圖

BackupNode機制

跟Mysql的Master-Slave機制類似, BackupNode是作為熱備而存在, 同步更新NameNode節點的數據。

NameNode HA機制

NameNode數據主要包含兩類:

  1. map<filename, list<block_id>> 即數據源信息

    這一類數據主要存儲在兩個文件中: fsimg, editlog, 如上所說SecondaryNameNode的作用就是定期merge這兩個文件。

    在HA機制中, 流程為將editlog寫入一個共享存儲, 一般為QJM(Quorum Journal Manager)節點, 一般為3個節點。active NN的editlog實時寫入qjm節點, standby的NN定期從editlog中同步數據到自己的節點信息當中。

    每次日志都有一個自增的epoch_id, jn會對比自己已有的epoch_id和NN給的epoch_id, 如果NN給的epoch_id比較小, 則會忽略該命令, 以此方式來達到避免腦裂問題。

    NN通過ZKFC(ZooKeeper Fail Controller)來控制當前到底是哪個節點為ActiveNameNode, 在每個NN上都單獨啟動了一個ZKFC的進程, 該進程一方面監控NN的狀態信息, 另一方面跟ZK保持連接說明自己的狀態。類似租約, 一旦NN節點掛了, ZK會自動更新該節點狀態信息, 同時通知另外的節點, 另外的節點就好接替前一個NN的工作。

  2. map<block_id, list<DataNode>> 即block_id和DataNode的對應關系

    這個本身信息是通過DataNode給NameNode上報來做到的, 增加了節點之后, DataNode跟每個NameNode都會上報一份信息。

    類似qjm, DataNode也會維護一個自增的id, 當有NN切換的時候, 也會增加這個id, DN會拒絕比當前id還要小的控制命令。

HA的數據流圖如下:

HA數據流圖

參考:

  1. 當前HDFS HA介紹. http://www.it165.net/admin/html/201407/3465.html
  2. HDFS HA進化論. http://www.bubuko.com/infodetail_124006.html

HDFS Federation

本身實現不復雜, 就是將原來所有信息都放在一個NameNode節點里, 變成了可以將NameNode信息拆分放到多個節點里, 一人分一個目錄。

注意有一個block pool的概念, 為了避免在分配DataNode上的打架, 為每個NameNode分配了一個專屬的block pool, 這樣大家就分開了, 需要一開始配置自己的NameNode需要多少空間, 即Namespace Volume。

如下圖:

Federation架構圖1

Federation架構圖2

參考:

  1. http://zh.hortonworks.com/blog/an-introduction-to-hdfs-federation/
  2. http://blog.csdn.net/strongerbit/article/details/7013221/
  3. https://issues.apache.org/jira/secure/attachment/12453067/high-level-design.pdf

HDFS distcp

distcp1:

  1. 利用mapreduce來傳輸文件

  2. 為了保證文件內block的有序性, 所以map最小粒度為文件

    問題出來了, 大文件會拖慢小文件, 導致整個拷貝效率不行

distcp2:

針對distcp1, 做了一些優化:

  1. 去掉了一些不必要的目錄檢查工作, 從而縮短了目錄檢查時間;
  2. 動態分配map task的工作量, 在運行過程中調整自己的任務量, 能優化部分distcp1的情況;
  3. 可對拷貝進行限速
  4. 支持HSFTP

fastcopy:

最主要是對federation機制的支持, 如果使用fastcp在同一個集群中不同的federation進行拷貝的時候, 則不需要再走一遍網絡和刪除, 只修改源數據即可, 但是distcp不行。

facebook的hadoop版本已經將fastcopy的這個特性集成到了distcp當中。

參考:

  1. distcp介紹. http://dongxicheng.org/hadoop-hdfs/hadoop-hdfs-distcp-fastcopy/

HDFS Balancer

是一個腳本, 該腳本做的事情很簡單:

  1. 從NameNode獲取DataNode數據分布信息
  2. 計算數據移動方案
  3. 執行移動方案
  4. 直到滿足平衡要求

平衡要求是該腳本的一個輸入(0~100), 代表不同機器的磁盤利用率差值。

注意:

  1. Balancer程序現在設計是不會將一個rack的數據移動到另外一個rack
  2. 也就是說跨rack的均衡是不能滿足的, 除非有修改后的Balancer程序
  3. 一般不要將Balancer放到NameNode上運行

數據流圖:

balancer數據平衡數據流

參考:

  1. balancer介紹. http://www.aboutyun.com/thread-7354-1-1.html

HDFS 快照

快照是給當前hdfs內容建立一個只讀備份, 可以針對整個hdfs或者某個目錄創建, 一般用於備份, 故障恢復, 避免人工故障等。

HDFS的快照有如下特點:

  1. 快照是瞬間創造的, 如果拋開inode的查詢時間, 只需要O(1)
  2. 快照創建以后需要額外的內存來存儲變化, 內存需要是O(M)
  3. 快照只是記錄了block_list和文件大小信息, 不做任何的實際數據拷貝
  4. 快照不會影響到現在數據的增刪改查, 查詢快照的時候, 會根據當前結果以及記錄的日志做減法來獲取快照數據

HDFS NFSv3

就是在本地通過掛載NFS訪問HDFS的文件

參考: http://blog.csdn.net/dmcpxy/article/details/18257065

HDFS dfsadmin

參考: http://blog.csdn.net/xiaojin21cen/article/details/42610697

hdfs 權限控制

認證:

支持兩種方式, simple和kerberos, 通過hadoop.security.authentication這個類配置。默認是simple模式, simple模式下, 用戶名為whoami, 組名為bash -c group

kerberos沒有研究。

授權:

針對每個目錄, 有讀寫一級可執行的權限設置, 權限有分為用戶級別, 組級別, 以及其他。

同時也可以通過設置ACL來針對一個目錄的某些用戶設置特殊權限。

參考: http://demo.netfoucs.com/skywalker_only/article/details/40709447

代碼閱讀筆記

讀取文件信息

客戶端代碼

  1. DFSClient.getFileInfo
  2. 到ClientProtocol類里查看getFileInfo
    是接口, 用ctl+T查看其子類列表, 找到ClientNamenodeProtocolTranslatorPB類
  3. 再看ClientNamenodeProtocolTranslatorPB.getFileInfo函數
    看到其調用了rpcProxy.getFileInfo類, 找rpcProxy的來源, 發現是構造函數傳進來的, 所以用ctl+alt+H來找到其反向調用關系
  4. NameNodeProxies.createNNProxyWithClientProtocol函數
    發現在該函數中調用了RPC.getProtocolProxy函數來獲取該proxy, 在獲得該proxy的時候傳入了系列NN服務相關配置, 以及SocketFactory
  5. 到RPC.getProtocolProxy函數
    發現其是調用了getProtocolEngine(XX).getProxy(XX)的函數, 那么先看setProtocolEngine函數
  6. 到RPC.setProtocolEngine函數中看
    發現其都設置的是RpcEngine的子類, 那么去看該RpcEngine的接口, 發現其實一個interface, 老辦法, 用ctl+T看其子類, 找到ProtobufRpcEngine子類
  7. 找ProtobufRpcEngine.getProxy函數
    發現其主要是使用了自己的一個Invoker的類, 該Invoker是一個動態代理類, 主要關注其invoke函數即可
  8. 看ProtobufRpcEngine.Invoker.invoke函數
    該函數就是各種網絡操作了, 可以看到他是將函數名拼成了一個字節流, 然后發給了NN, 然后hang住等待NN的返回結果

服務端代碼

  1. ClientProtocol類是用來通信的類, 客戶端和服務端都會用到, 直接看其子類即可
    看到其子類有一個NameNodeRpcServer, 看起來肯定就是NN服務端這頭的代碼了
  2. NameNodeRpcServer.getFileInfo
    發現其實調用了namesystem.getFileInfo函數
  3. namesystem.getFileInfo函數
    一層一層往下面調用, 就可以找到其最終的邏輯了

NN啟動代碼

直接進入NameNode.main查看

  1. 參數獲取和查看我們略過, 我們着重關注后面的邏輯
  2. createNameNode
    調用了構造函數: NameNode(conf), conf已經根據argv參數初始化好了
    該函數又調用了NameNode.initialize函數
  3. initialize(conf)
    1. 啟動了http接口, 先啟動http接口應該是說可以通過http接口來查看啟動狀態
    2. 調用loadNamesystem函數
    3. 該函數又調用了FSNamesystem.loadNamesystem函數
      FSNamesystem就是非常重要的類了, 基本上所有的NN的核心數據結構都放在這個類里面了
    4. 主要關注該類的loadFSImage函數
  4. namenode.join()

參考文章

  1. hdfs流程簡介. http://www.cnblogs.com/forfuture1978/archive/2010/03/14/1685351.html
  2. hdfs vs kfs. http://blog.csdn.net/Cloudeep/article/details/4467238
  3. hdfs的缺陷. http://www.cnblogs.com/wycg1984/archive/2010/03/20/1690281.html
  4. hdfs配置. http://cqfish.blog.51cto.com/622299/207766
  5. hdfs看分布式文件系統設計需求. http://dennis-zane.javaeye.com/blog/228537
  6. 利用Java API訪問hdfs文件. http://blog.csdn.net/zhangzhaokun/article/details/5597433
  7. hdfs重大性能殺手——shell. http://blog.csdn.net/fly542/article/details/6819945
  8. DataNode的stale狀態. http://www.tuicool.com/articles/RneQve
  9. hdfs源碼閱讀. http://www.linuxidc.com/Linux/2012-03/55966.htm


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM