天天看點

《Hadoop海量資料處理:技術詳解與項目實戰》一 3.2 HDFS讀取檔案和寫入檔案

本節書摘來異步社群《hadoop海量資料處理:技術詳解與項目實戰》一書中的第3章,第3.2節,作者: 範東來 責編: 楊海玲,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

hadoop海量資料處理:技術詳解與項目實戰

我們知道在hdfs中,namenode作為叢集的大腦,儲存着整個檔案系統的中繼資料,而真正資料是存儲在datanode的塊中。本節将介紹hdfs如何讀取和寫入檔案,組成同一檔案的塊在hdfs的分布情況如何影響hdfs讀取和寫入速度。

3.2.1 塊的分布

hdfs會将檔案切片成塊并存儲至各個datanode中,檔案資料塊在hdfs的布局情況由namenode和hdfs-site.xml中的配置dfs.replication共同決定。dfs.replication表示該檔案在hdfs中的副本數,預設為3,即有兩份備援。

圖3-5為dfs.replication為1的分布情況,即沒有備援。圖3-6為dfs.replication為2的分布情況,即有一份備援。

圖像說明文字

《Hadoop海量資料處理:技術詳解與項目實戰》一 3.2 HDFS讀取檔案和寫入檔案

namenode如何選擇在哪個datanode存儲副本?這裡需要在可靠性、寫入速度和讀取速度之間進行一些取舍。如果将所有副本都存儲在一個節點之中,那麼寫入的帶寬損失将是最小的,因為複制管道都是在單一節點上運作。但這樣無論副本數設為多少,hdfs都不提供真實的備援,因為該檔案的所有資料都在一個節點之上,那麼如果該節點發生故障的話,該檔案的資料将會丢失。如果将副本放在不同資料中心,可以最大限度地提高備援,但是對帶寬的損耗非常大。即使在同一資料中心,也有不同的副本分布政策。其實,在釋出的hadoop 0.17.0版本中改變了資料布局政策來輔助保持資料塊在叢集内分布相對均勻。從0.21.0版本開始,可即時選擇塊的分布政策。

hadoop的預設布局是在hdfs用戶端節點上放第一個副本,但是由于hdfs用戶端有可能運作于叢集之外,就随機選擇一個節點,不過hadoop會盡量避免選擇那些存儲太滿或者太忙的節點。第二個副本放在與第一個不同且随機另外選擇的機架中的節點上。第三個副本與第二個副本放在相同的機架,且随機選擇另外一個節點。其他副本(如果dfs.replication大于3)放在叢集随機選擇的節點上,hadoop也會盡量避免在相同的機架上放太多副本。當namenode按照上面的政策標明副本存儲的位置後,就會根據叢集網絡拓撲圖建立一個管道。假設dfs.replication = 3,則如圖3-7所示。

《Hadoop海量資料處理:技術詳解與項目實戰》一 3.2 HDFS讀取檔案和寫入檔案

這樣的方法不僅提供了很好的資料備援性(如果可能,塊存儲在兩個機架中)并實作很好的負載均衡,包括寫入帶寬(寫入操作隻需要周遊一個交換機)、讀取性能(可以從兩個機架中進行選擇讀取)和叢集中塊的均勻分布。

hdfs用戶端可以通過多種不同的方式(如指令行、java api等)對hdfs進行讀寫操作,這些操作都遵循同樣的流程。hdfs用戶端需要使用到hadoop庫函數,函數庫封裝了大部分與namenode和datanode通信相關的細節,同時也考慮了分布式檔案系統在諸多場景的錯誤處理機制。

假設在hdfs中存儲了一個檔案/user/test.txt,hdfs用戶端要讀取該檔案,hadoop用戶端程式庫是必不可少的。如圖3-8所示,hdfs用戶端首先要通路namenode,并告訴它所要讀取的檔案,在這之前,hdfs會對客戶的身份資訊進行驗證。驗證的方式有兩種:一種是通過信任的用戶端,由其指定使用者名;第二種方式是通過諸如kerberos等強制驗證機制來完成。接下來還需要檢查檔案的所有者及其設定的通路權限。當檔案确實存在,且該使用者對其有通路權限,這時namenode會告訴hdfs用戶端這個檔案的第一個資料塊的标号以及儲存有該資料塊的datanode清單。這個清單是datanode與hdfs用戶端間的距離進行了排序。有了資料塊标号和datanode的主機名,hdfs用戶端便可以直接通路最合适的datanode,讀取所需要的資料塊。這個過程會一直重複直到該檔案的所有資料塊讀取完成或hdfs用戶端主動關閉了檔案流。特殊的情況,如果該hdfs用戶端是叢集中的datanode時,該節點将從本地datanode中讀取資料。

《Hadoop海量資料處理:技術詳解與項目實戰》一 3.2 HDFS讀取檔案和寫入檔案

hdfs的資料寫操作相對複雜些,以hdfs用戶端向hdfs建立一個新檔案為例,如圖3-9所示。

《Hadoop海量資料處理:技術詳解與項目實戰》一 3.2 HDFS讀取檔案和寫入檔案

首先hdfs用戶端通過hdfs相關api發送請求,打開一個要寫入的檔案,如果該使用者有寫入檔案的權限,那麼這一請求将被送達namenode,并建立該檔案的中繼資料。但此時建立立的檔案中繼資料并未和任何資料塊相關聯,這時hdfs用戶端會收到“打開檔案成功”的響應,接着就可以寫入資料了。當用戶端将資料寫入流時,資料會被自動拆分成資料包,并将資料包儲存在記憶體隊列中。用戶端有一個獨立的線程,它從隊列中讀取資料包,并向namenode請求一組datanode清單,以便寫入下一個資料塊的多個副本。接着,hdfs用戶端将直接連接配接到清單中的第一個datanode,而該datanode又連接配接到第二個datanode,第二個又連接配接到第三個,如此就建立了資料塊的複制管道。複制管道中的每一個datanode都會确認所收到的資料包已經成功寫入磁盤。hdfs用戶端應用程式維護着一個清單,記錄着哪些資料包尚未收到确認資訊。每收到一個響應,用戶端便知道資料已經成功地寫入管道中的一個datanode。當資料塊被寫入清單中的datanode中時,hdfs用戶端将重新向namenode申請下一組datanode。最終,用戶端将剩餘資料包寫入全部磁盤,關閉資料管道并通知namenode檔案寫操作已經完成。

如果寫入的時候,複制管道中的某一個datanode無法将資料寫入磁盤(如datanode當機)。發生這種錯誤時,管道會立即關閉,已發送的但尚未收到确認的資料包會被退回到隊列中,以確定管道中錯誤節點的下遊節點可以得到資料包。而剩下的健康的datanode中,正在寫入的資料塊會被配置設定新的blk_id。這樣,當發生故障的資料節點恢複後,備援的資料塊就會因為不屬于任何檔案而被自動丢棄,由剩餘datanode節點組成的新複制管道會重新開放,寫入操作得以繼續,寫操作将繼續直至檔案關閉。namenode如果發現檔案的某個資料塊正在通過複制管道進行複制,就會異步地建立一個新的複制塊,這樣,即便hdfs的多個datanode發生錯誤,hdfs用戶端仍然可以從資料塊的副本中恢複資料,前提是滿足最少數目要求的資料副本(dfs.replication.min)已經被正确寫入(dfs.replication.min配置預設為1)。

hadoop使用者都希望hdfs在讀寫資料時,資料不會有任何丢失或者損壞。但是在實際情況中,如果系統需要處理的資料量超過hdfs能夠處理的極限,那麼資料被損壞的機率還是很高的。

檢測資料是否損壞的常用措施是,在資料第一次引入系統時計算校驗和,并在資料通過一個不可靠的通道進行資料傳輸時再次計算校驗和,如果發現兩次校驗和不一樣,那麼可以判定,資料已經損壞。校驗和技術并不能修複資料,隻能檢測出資料是否已經損壞1。

hdfs也是采用校驗和技術判斷資料是否損壞,hdfs會對寫入的所有資料計算校驗和,并在讀取資料的時驗證校驗和,它針對由core-site.xml檔案的io.bytes.per.checksum配置項指定位元組的資料計算校驗和,預設為512位元組。

datanode負責驗證收到的資料的校驗和,并且如果該校驗和正确,則儲存收到的資料。datanode在收到用戶端的資料或複制其他datanode的資料時執行這個操作。正在寫資料的hdfs用戶端将資料及其校驗和發送到由一系列datanode組成的複制管道,如圖3-9所示,管道中最後一個datanode負責驗證校驗和。如果datanode檢測到錯誤,hdfs用戶端便會收到一個校驗和異常,可以在應用程式中捕獲該異常,并做相應的處理,例如重新嘗試寫入。

hdfs用戶端從datanode讀取資料時,也會驗證校驗和,将它們與datanode中存儲的校驗和進行比較。每個datanode均儲存有一個用于驗證的校驗和日志,是以它知道每個資料塊的最後一次驗證時間。用戶端成功驗證一個資料塊後,會告訴這個datanode,datanode由此更新日志。

不隻是用戶端在讀取資料和寫入資料時會驗證校驗和,每個datanode也會在一個背景線程運作一個datablockscanner,定期驗證存儲在這個datanode上的所有資料塊。

由于hdfs存儲着每個資料塊的副本,是以當一個資料塊損壞時,hdfs可以通過複制完好的該資料塊副本來修複損壞的資料塊,進而得到一個新的、完好無損的副本。大緻的步驟是,hdfs用戶端在讀取資料塊時,如果檢測到錯誤,則向namenode報告已損壞的資料塊以及嘗試讀取該資料塊的datanode,最後才抛出checksumexception異常。namenode将這個已損壞的資料塊标記為已損壞。之後,它安排這個資料塊的一個副本複制到另一個datanode,如此一來,資料塊的副本數又回到了配置的水準。最後,已損壞的資料塊副本便會被删除。