NameNode與DataNode的工作原理剖析
作者:尹正傑
版權聲明:原創作品,謝絕轉載!否則將追究法律責任。
一.HDFS寫數據流程
1>.客戶端通過Distributed FileSystem模塊向NameNode請求上傳文件,NameNode檢查目標文件是否已存在,父目錄是否存在。 2>.NameNode返回是否可以上傳。 3>.客戶端請求第一個 Block上傳到哪幾個DataNode服務器上。 4>.NameNode返回3個DataNode節點,分別為DataNode1、DataNode2、DataNode3。 5>.客戶端通過FSDataOutputStream模塊請求DataNode1上傳數據,DataNode1收到請求會繼續調用DataNode2,然后DataNode2調用DataNode3,將這個通信管道建立完成。 6>.DataNode1、DataNode2、DataNode3逐級應答客戶端。 7>.客戶端開始往DataNode1上傳第一個Block(先從磁盤讀取數據放到一個本地內存緩存),以Packet為單位,DataNode1收到一個Packet就會傳給DataNode2,DataNode2傳給DataNode3;DataNode1每傳一個packet會放入一個應答隊列等待應答。 8>.當一個Block傳輸完成之后,客戶端再次請求NameNode上傳第二個Block的服務器。(重復執行3-7步)。
問第一個問題:請詳細說明上面的第5步,DFSOutputStream是基於什么為單位上傳數據的呢? DFSOutputStream會將文件分割成packets數據包,然后將這些packets寫到其內部的一個叫做data queue(數據隊列)。data queue會向NameNode節點請求適合存儲數據副本的DataNode節點的列表,然后這些DataNode之前生成一個Pipeline數據流管道,我們假設副本集參數被設置為3,那么這個數據流管道中就有三個DataNode節點。 問第二個問題: 在寫數據的過程中,如果Pipeline數據流管道中的一個DataNode節點寫失敗了會發生什問題、需要做哪些內部處理呢? 首先,Pipeline數據流管道會被關閉,ack queue中的packets會被添加到data queue的前面以確保不會發生packets數據包的丟失; 接着,在正常的DataNode節點上的以保存好的block的ID版本會升級——這樣發生故障的DataNode節點上的block數據會在節點恢復正常后被刪除,失效節點也會被從Pipeline中刪除; 最后,剩下的數據會被寫入到Pipeline數據流管道中的其他兩個節點中。 如果Pipeline中的多個節點在寫數據是發生失敗,那么只要寫成功的block的數量達到dfs.replication.min(默認為1,Hadoop2.9.2版本已經將其更名為dfs.namenode.replication.min),那么就任務是寫成功的,然后NameNode后通過一步的方式將block復制到其他節點,最后事數據副本達到dfs.replication參數配置的個數。

整個寫流程如下: 第一步: 客戶端調用DistributedFileSystem的create()方法,開始創建新文件:DistributedFileSystem創建DFSOutputStream,產生一個RPC調用,讓NameNode在文件系統的命名空間中創建這一新文件; 第二步: NameNode接收到用戶的寫文件的RPC請求后,首先要執行各種檢查,如客戶是否有相關的創佳權限和該文件是否已存在等,檢查都通過后才會創建一個新文件,並將操作記錄到編輯日志,然后DistributedFileSystem會將DFSOutputStream對象包裝在FSDataOutStream實例中,返回客戶端;否則文件創建失敗並且給客戶端拋IOException。 第三步: 客戶端開始寫文件:DFSOutputStream會將文件分割成packets數據包,然后將這些packets寫到其內部的一個叫做data queue(數據隊列)。data queue會向NameNode節點請求適合存儲數據副本的DataNode節點的列表,然后這些DataNode之前生成一個Pipeline數據流管道,我們假設副本集參數被設置為3,那么這個數據流管道中就有三個DataNode節點。 第四步: 首先DFSOutputStream會將packets向Pipeline數據流管道中的第一個DataNode節點寫數據,第一個DataNode接收packets然后把packets寫向Pipeline中的第二個節點,同理,第二個節點保存接收到的數據然后將數據寫向Pipeline中的第三個DataNode節點。 第五步: DFSOutputStream內部同樣維護另外一個內部的寫數據確認隊列——ack queue。當Pipeline中的第三個DataNode節點將packets成功保存后,該節點回向第二個DataNode返回一個確認數據寫成功的信息,第二個DataNode接收到該確認信息后在當前節點數據寫成功后也會向Pipeline中第一個DataNode節點發送一個確認數據寫成功的信息,然后第一個節點在收到該信息后如果該節點的數據也寫成功后,會將packets從ack queue中將數據刪除。 在寫數據的過程中,如果Pipeline數據流管道中的一個DataNode節點寫失敗了會發生什問題、需要做哪些內部處理呢?如果這種情況發生,那么就會執行一些操作: 首先,Pipeline數據流管道會被關閉,ack queue中的packets會被添加到data queue的最前面以確保不會發生packets數據包的丟失; 接着,在正常的DataNode節點上的以保存好的block的ID版本會升級——這樣發生故障的DataNode節點上的block數據會在節點恢復正常后被刪除,失效節點也會被從Pipeline中刪除; 最后,剩下的數據會被寫入到Pipeline數據流管道中的其他兩個節點中。 如果Pipeline中的多個節點在寫數據是發生失敗,那么只要寫成功的block的數量達到dfs.replication.min(默認為1),那么就任務是寫成功的,然后NameNode后通過一步的方式將block復制到其他節點,最后使數據副本達到dfs.replication參數配置的個數。 因此,我們不得不懷疑該機制是否會導致一定的數據重復呢? 第六步: 完成寫操作后,客戶端調用close()關閉寫操作,刷新數據; 第七步: 在數據刷新完后NameNode后關閉寫操作流。到此,整個寫操作完成。 參考鏈接:https://flyingdutchman.iteye.com/blog/1900536
上面我們說到的dfs.replication.min屬性官方已經被更名為dfs.namenode.replication.min,因此我們直接去官方文檔查閱dfs.replication.min肯能會查不到,更多參數變更請參考官方說明:http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/DeprecatedProperties.html(不推薦使用的屬性下表列出了此版本的Hadoop中不推薦使用的配置屬性名稱及其替換。)
二.機架感知
對於常見情況,當復制因子為3時,HDFS的放置策略是將一個副本放在本地機架中的一個節點上,另一個放在本地機架中的另一個節點上,最后一個放在不同機架中的另一個節點上。副本節點的選擇大致為:
1>.第一個副本在Client所處的節點上,如果客戶端在集群外,隨機選一個;
2>.第二個副本和第一個副本位於相同機架,隨機節點;
3>.第三個部分位於不同幾家,隨機節點; 感興趣的小伙伴可以參考官方文檔: 參考一:http://hadoop.apache.org/docs/r2.9.2/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication
參考二:http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/RackAwareness.html
三.HDFS讀數據流程
1>.客戶端通過Distributed FileSystem向NameNode請求下載文件,NameNode通過查詢元數據,找到文件塊所在的DataNode地址。
2>.挑選一台DataNode(就近原則,然后隨機)服務器,請求讀取數據。
3>.DataNode開始傳輸數據給客戶端(從磁盤里面讀取數據輸入流,以Packet為單位來做校驗)。
4>.客戶端以Packet為單位接收,先在本地緩存,然后寫入目標文件。
四.DataNode工作原理
1>.一個數據塊在DataNode上以文件形式存儲在磁盤上,包括兩個文件,一個是數據本身,一個是元數據包括數據塊的長度,塊數據的校驗和,以及時間戳。
2>.DataNode啟動后向NameNode注冊,通過后,周期性(1小時)的向NameNode上報所有的塊信息。
3>.心跳是每3秒一次,心跳返回結果帶有NameNode給該DataNode的命令如復制塊數據到另一台機器,或刪除某個數據塊。如果超過10分鍾沒有收到某個DataNode的心跳,則認為該節點不可用。
4>.集群運行中可以安全加入和退出一些機器。
五.數據完整性
1>.當DataNode讀取block的時候,它會計算checksum; 2>.如果計算后的checksum,與block創建時值不一樣,說明block已經損壞; 3>.client讀取其他DataNode上的block; 4>.datanode在其文件創建后周期驗證checksum;
六.掉線時限參數設置
datanode進程死亡或者網絡故障造成datanode無法與namenode通信,namenode不會立即把該節點判定為死亡,要經過一段時間,這段時間暫稱作超時時長。HDFS默認的超時時長為10分鍾+30秒。如果定義超時時間為timeout,則超時時長的計算公式為:“timeout = 2 * dfs.namenode.heartbeat.recheck-interval + 10 * dfs.heartbeat.interval ”。
而默認的"dfs.namenode.heartbeat.recheck-interval"大小為5分鍾,"dfs.heartbeat.interval"默認為3秒。需要注意的是hdfs-site.xml配置文件中"heartbeat.recheck.interval"的單位為毫秒,"dfs.heartbeat.interval"的單位為秒。

[root@node101.yinzhengjie.org.cn ~]# cat /yinzhengjie/softwares/hadoop-2.9.2/etc/hadoop/hdfs-site.xml <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property> <name>dfs.namenode.checkpoint.period</name> <value>3600</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>/data/hadoop/hdfs/dfs/name</value> </property> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.heartbeat.recheck-interval</name> <value>300000</value> </property> <property> <name> dfs.heartbeat.interval </name> <value>3</value> </property> </configuration> <!-- hdfs-site.xml 配置文件的作用: #HDFS的相關設定,如文件副本的個數、塊大小及是否使用強制權限等,此中的參數定義會覆蓋hdfs-default.xml文件中的默認配置. dfs.namenode.checkpoint.period 參數的作用: #兩個定期檢查點之間的秒數,默認是3600,即1小時。 dfs.namenode.name.dir 參數的作用: #指定namenode的工作目錄,默認是file://${hadoop.tmp.dir}/dfs/name,namenode的本地目錄可以配置成多個,且每個目錄存放內容相同,增加了可靠性。建議配置的多目錄用不同磁盤掛在,這樣可以提升IO性能! dfs.replication 參數的作用: #為了數據可用性及冗余的目的,HDFS會在多個節點上保存同一個數據塊的多個副本,其默認為3個。而只有一個節點的偽分布式環境中其僅用 保存一個副本即可,這可以通過dfs.replication屬性進行定義。它是一個軟件級備份。 dfs.heartbeat.interval 參數的作用: #設置心跳檢測時間 dfs.namenode.heartbeat.recheck-interval和dfs.heartbeat.interval 參數的作用: #設置HDFS中NameNode和DataNode的超時時間,計算公式為:timeout = 2 * dfs.namenode.heartbeat.recheck-interval + 10 * dfs.heartbeat.interval。 --> [root@node101.yinzhengjie.org.cn ~]#
七.DataNode的目錄結構
和NameNode不同的是,DataNode的存儲目錄是初始階段自動創建的,不需要額外格式化。
1>.查看DataNode目錄下對應的版本號("${hadoop.tmp.dir}/dfs/data/current/VERSION")

[root@node101.yinzhengjie.org.cn ~]# ll /data/hadoop/hdfs/dfs/data/current/ total 8 drwx------. 4 root root 4096 Apr 12 18:44 BP-883662044-172.30.1.101-1555064443805 -rw-r--r--. 1 root root 229 Apr 12 18:44 VERSION [root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data/current/VERSION #Fri Apr 12 18:44:23 CST 2019 storageID=DS-e181274d-eace-44c1-b001-ac26fbfa3f8c #存儲id號 clusterID=CID-e7603940-eaba-4ce6-9ecd-3a449027b432 #集群id,全局唯一 cTime=0 #標記了datanode存儲系統的創建時間,對於剛剛格式化的存儲系統,這個屬性為0;但是在文件系統升級之后,改值會更新到新的時間戳。 datanodeUuid=a7c28347-2816-47ee-a3f9-153d11e162bf #datanode的唯一標識碼 storageType=DATA_NODE #存儲類型 layoutVersion=-57 #一般情況下是一個負數,通常只有HDFS增加新特性時才會更新這個版本號。 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#

[root@node102.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data/current/VERSION #Fri Apr 12 18:44:23 CST 2019 storageID=DS-e181274d-eace-44c1-b001-ac26fbfa3f8c clusterID=CID-e7603940-eaba-4ce6-9ecd-3a449027b432 cTime=0 datanodeUuid=a7c28347-2816-47ee-a3f9-153d11e162bf storageType=DATA_NODE layoutVersion=-57 [root@node102.yinzhengjie.org.cn ~]#

[root@node103.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data/current/VERSION #Fri Apr 12 18:44:23 CST 2019 storageID=DS-e181274d-eace-44c1-b001-ac26fbfa3f8c clusterID=CID-e7603940-eaba-4ce6-9ecd-3a449027b432 cTime=0 datanodeUuid=a7c28347-2816-47ee-a3f9-153d11e162bf storageType=DATA_NODE layoutVersion=-57 [root@node103.yinzhengjie.org.cn ~]#
2>.查看DataNode目錄下對應數據塊的版本號("${hadoop.tmp.dir}/dfs/data/current/BP-*/current/VERSION")
[root@node101.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data/current/BP-883662044-172.30.1.101-1555064443805/current/VERSION #Fri Apr 12 18:44:23 CST 2019 namespaceID=1161472027 #是datanode首次訪問namenode的時候從namenode處獲取的storageID對每個datanode來說是唯一的(但對於單個datanode中所有存儲目錄來說則是相同的),namenode可以用這個屬性來區分不同datanode。 cTime=1555064443805 #標記了datanode存儲系統的創建時間,對於剛剛格式化的存儲系統,這個屬性為0;但是在文件系統升級之后,該值會更新到新的時間戳。 blockpoolID=BP-883662044-172.30.1.101-1555064443805 #標識一個block pool,並且是跨集群的全局唯一。當一個新的NameSpace被創建的時候(format過程的一部分)會創建並持久化一個唯一ID,在創建過程構建全局唯一的BlockPoolID此人為的配置更可靠一些。NameNode將BlockPoolID持久化到磁盤中,在后續的啟動過程中,會再次load並使用。 layoutVersion=-57 #改值是一個負整數。通常只有HDFS增加新特性時才會更新這個版本號。 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#

[root@node102.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data/current/BP-883662044-172.30.1.101-1555064443805/current/VERSION #Fri Apr 12 18:44:23 CST 2019 namespaceID=1161472027 cTime=1555064443805 blockpoolID=BP-883662044-172.30.1.101-1555064443805 layoutVersion=-57 [root@node102.yinzhengjie.org.cn ~]# [root@node102.yinzhengjie.org.cn ~]#

[root@node103.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data/current/BP-883662044-172.30.1.101-1555064443805/current/VERSION #Fri Apr 12 18:44:23 CST 2019 namespaceID=1161472027 cTime=1555064443805 blockpoolID=BP-883662044-172.30.1.101-1555064443805 layoutVersion=-57 [root@node103.yinzhengjie.org.cn ~]#
八.DataNode多目錄配置(我們在主節點做了任何修改后,最好同步到整個集群中去,否則可能會導致部分節點啟動失敗!)
DataNode也可以配置成多個目錄,每個目錄存儲的數據不一樣。即:數據不是副本!切記,DataNode的多目錄配置和NameNode的多目錄配置效果是不一樣的! NameNode配置多目錄是為了把元數據存儲多份,達到配置備份的目的。
關於已經有數據的HDFS集群中,配置案例如下:

[root@node101.yinzhengjie.org.cn ~]# cat /yinzhengjie/softwares/hadoop-2.9.2/etc/hadoop/core-site.xml <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://node101.yinzhengjie.org.cn:8020</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/data/hadoop/hdfs</value> </property> </configuration> <!-- core-site.xml配置文件的作用: 用於定義系統級別的參數,如HDFS URL、Hadoop的臨時目錄以及用於rack-aware集群中的配置文件的配置等,此中的參數定義會覆蓋core-default.xml文件中的默認配置。 fs.defaultFS 參數的作用: #聲明namenode的地址,相當於聲明hdfs文件系統。 hadoop.tmp.dir 參數的作用: #聲明hadoop工作目錄的地址。 --> [root@node101.yinzhengjie.org.cn ~]#

[root@node101.yinzhengjie.org.cn ~]# cat /yinzhengjie/softwares/hadoop-2.9.2/etc/hadoop/hdfs-site.xml <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <configuration> <property> <name>dfs.namenode.checkpoint.period</name> <value>3600</value> </property> <property> <name>dfs.namenode.name.dir</name> <value>file:///${hadoop.tmp.dir}/dfs/namenode1,file:///${hadoop.tmp.dir}/dfs/namenode2,file:///${hadoop.tmp.dir}/dfs/namenode3</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value> </property> <property> <name>dfs.replication</name> <value>2</value> </property> <property> <name>dfs.namenode.heartbeat.recheck-interval</name> <value>300000</value> </property> <property> <name> dfs.heartbeat.interval </name> <value>3</value> </property> </configuration> <!-- hdfs-site.xml 配置文件的作用: #HDFS的相關設定,如文件副本的個數、塊大小及是否使用強制權限等,此中的參數定義會覆蓋hdfs-default.xml文件中的默認配置. dfs.namenode.checkpoint.period 參數的作用: #兩個定期檢查點之間的秒數,默認是3600,即1小時。 dfs.namenode.name.dir 參數的作用: #指定namenode的工作目錄,默認是file://${hadoop.tmp.dir}/dfs/name,namenode的本地目錄可以配置成多個,且每個目錄存放內容相同,增加了可靠性。建議配置的多目錄用不同磁盤掛在,這樣可以提升IO性能! dfs.datanode.data.dir 參數的作用: #指定datanode的工作目錄,議配置的多目錄用不同磁盤掛在,這樣可以提升IO性能!但是多個目錄存儲的數據並不相同喲!而是把數據存放在不同的目錄,當namenode存儲數據時效率更高! dfs.replication 參數的作用: #為了數據可用性及冗余的目的,HDFS會在多個節點上保存同一個數據塊的多個副本,其默認為3個。而只有一個節點的偽分布式環境中其僅用 保存一個副本即可,這可以通過dfs.replication屬性進行定義。它是一個軟件級備份。 dfs.heartbeat.interval 參數的作用: #設置心跳檢測時間 dfs.namenode.heartbeat.recheck-interval和dfs.heartbeat.interval 參數的作用: #設置HDFS中NameNode和DataNode的超時時間,計算公式為:timeout = 2 * dfs.namenode.heartbeat.recheck-interval + 10 * dfs.heartbeat.interval。 --> [root@node101.yinzhengjie.org.cn ~]#

[root@node101.yinzhengjie.org.cn ~]# scp -r /yinzhengjie/softwares/hadoop-2.9.2/ node102.yinzhengjie.org.cn:/yinzhengjie/softwares/

[root@node101.yinzhengjie.org.cn ~]# scp -r /yinzhengjie/softwares/hadoop-2.9.2/ node103.yinzhengjie.org.cn:/yinzhengjie/softwares/

[root@node101.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data1/current/VERSION #Mon Apr 15 15:47:43 CST 2019 storageID=DS-a29dd65a-de0e-44b1-b51b-5d537f0ab7f1 clusterID=CID-377f58b3-a3a2-4ca7-bf72-7d47714cf9cd cTime=0 datanodeUuid=d1d3a605-0218-42b9-9638-255343195296 storageType=DATA_NODE layoutVersion=-57 [root@node101.yinzhengjie.org.cn ~]#

[root@node101.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data2/current/VERSION #Mon Apr 15 15:47:43 CST 2019 storageID=DS-9f2fa0b3-e9d7-4743-a9e8-ff2d81370200 clusterID=CID-377f58b3-a3a2-4ca7-bf72-7d47714cf9cd cTime=0 datanodeUuid=d1d3a605-0218-42b9-9638-255343195296 storageType=DATA_NODE layoutVersion=-57 [root@node101.yinzhengjie.org.cn ~]#

[root@node102.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data1/current/VERSION #Mon Apr 15 15:47:43 CST 2019 storageID=DS-053dada1-36dd-490b-a1d5-1a523bcfc6f3 clusterID=CID-377f58b3-a3a2-4ca7-bf72-7d47714cf9cd cTime=0 datanodeUuid=b43206d7-eb51-48b5-b269-6bd6502b5f9f storageType=DATA_NODE layoutVersion=-57 [root@node102.yinzhengjie.org.cn ~]#

[root@node102.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data2/current/VERSION #Mon Apr 15 15:47:43 CST 2019 storageID=DS-28a3e682-3ae8-4ce2-abf5-6691b669ef1a clusterID=CID-377f58b3-a3a2-4ca7-bf72-7d47714cf9cd cTime=0 datanodeUuid=b43206d7-eb51-48b5-b269-6bd6502b5f9f storageType=DATA_NODE layoutVersion=-57 [root@node102.yinzhengjie.org.cn ~]#

[root@node103.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data1/current/VERSION #Mon Apr 15 15:47:43 CST 2019 storageID=DS-e55b7230-fc9b-4122-a19d-30cb5855d455 clusterID=CID-377f58b3-a3a2-4ca7-bf72-7d47714cf9cd cTime=0 datanodeUuid=ed3bea6a-f5cd-45e9-8302-6de2106ec863 storageType=DATA_NODE layoutVersion=-57 [root@node103.yinzhengjie.org.cn ~]#

[root@node103.yinzhengjie.org.cn ~]# cat /data/hadoop/hdfs/dfs/data2/current/VERSION #Mon Apr 15 15:47:43 CST 2019 storageID=DS-3d59f0c6-ebbf-4f3d-b470-256c01a200d4 clusterID=CID-377f58b3-a3a2-4ca7-bf72-7d47714cf9cd cTime=0 datanodeUuid=ed3bea6a-f5cd-45e9-8302-6de2106ec863 storageType=DATA_NODE layoutVersion=-57 [root@node103.yinzhengjie.org.cn ~]#
九.Hadoop的集群管理之服役和退役
詳情請參考:Hadoop的集群管理之服役和退役 。