标簽: 大資料 hadoop
[toc]
hdfs是個分布式檔案系統,包含幾個特點(差別于普通分布式檔案系統):<code>高容錯</code>、<code>高吞吐</code>。高容錯可以使得系統部署在廉價硬體上,而高吞吐則非常适合做大規模資料集的應用。
硬體失效是常态而不是特例。一個hdfs叢集可能包含了成百上千的伺服器,每個都會存儲檔案系統的部分資料。而大量的元件就會導緻元件出錯的機率非常高,而這也意味着hdfs的部分元件會經常不工作。是以,檢查缺陷和快速自動地恢複就成了hdfs的核心架構目标。
運作在hdfs上的應用程式需要流式通路資料集的能力。它們不是普通的運作在普通檔案系統上的程式。hdfs被設計用來應對批量計算的場景,而不是用來和使用者互動。重點是資料通路的高吞吐而不是低延遲。posix引入了大量的硬性需求來限制應用程式,而這些需求不是hdfs的目标需求。posix語義在一些關鍵領域被認為可以提高資料吞吐率。
運作在hdfs上的程式擁有大規模的資料集。一個hdfs檔案可能是gb級别或是tb級别的存儲。是以hdfs被調優為存儲大檔案。它應該提供高聚合的資料帶寬并且可以在單個叢集内擴充到其他的上百上千的節點。程式應該支援在單執行個體中存在千萬級别的檔案。
hdfs程式需要一個一次寫入多次讀出的檔案通路模型。一旦一個檔案被建立、寫入資料然後關閉,這個檔案應該不再需要被改動。此假設簡化了資料一緻性的問題,并且支援了資料的高吞吐。一個map/reduce程式或者一個網絡爬蟲程式就非常符合這種模型。未來有計劃支援對于檔案的追加寫。
一個程式如果在運作計算任務時能更貼近其依賴的資料,那麼計算會更高效。尤其是在資料集規模很大時該效應更加明顯。因為這會最小化網絡消耗而增加系統整體的吞吐能力。這一假設就是:把計算靠近資料要比把資料靠近計算成本更低。hdfs提供給應用程式接口來做到移動程式使其離資料更近。
hdfs被設計為可以很容易的從一個平台移植到另一個平台。這有利于推廣hdfs,使其作為廣泛首選的大資料集應用的平台。
hdfs是一個主從master/slave架構。一個hdfs叢集包含一個namenode,這是一個master伺服器,用來管理檔案系統的命名空間以及調節用戶端對檔案的通路。除此,會有一堆datanode,通常是叢集裡每個節點一個datanode,管理在此節點上的資料存儲。hdfs對外暴露一個檔案系統命名空間,并允許使用者資料以檔案的形式存儲。在内部,一個檔案被分成一個或多個塊并且這些塊被存儲在一組datanode上。namenode來執行檔案系統命名空間的操作比如打開、關閉、重命名檔案和目錄。namenode同時也負責決策将資料塊映射到對應的datanode。而datanode負責服務檔案系統用戶端發出的讀寫請求。datanode同時也負責接受namenode的指令來進行資料塊的建立、删除和複制。

namenode和datanode都是被設計為在普通pc機上運作的軟體程式。這些機器最典型的就是運作在一個gnu/linux作業系統上。hdfs是java寫的;任何支援java的機器都可以運作namenode或者datanode。java語言本身的可移植性意味着hdfs可以被廣泛的部署在不同的機器上。一個典型的部署就是一台專用機器來運作namenode。叢集中的其他機器每台運作一個datanode執行個體。該架構并不排除在同一台機器上運作多個datanode執行個體,但在實際的部署中很少的情況下會這麼做。
單一namenode的設計極大的簡化了叢集的系統架構。namenode是所有hdfs中繼資料的仲裁和存儲庫。系統被設計為使用者資料從來不會流經namenode。
hdfs支援傳統的分層檔案組織。使用者或者應用程式可以建立目錄,并且在目中存儲檔案。檔案系統命名空間層級與大多數檔案系統都類似;使用者可以建立和移除檔案,在目錄間移動檔案,或者重命名一個檔案。hdfs還沒有實作使用者配額或者通路權限。hdfs不支援硬連結或者軟連接配接。當然hdfs不排除會支援這些feature。
namenode負責維護檔案系統命名空間。任何對檔案系統命名空間或者其屬性的改動都會被namenode記錄。應用程式可以聲明hdfs檔案的副本數。一個檔案的副本數被稱為該檔案的複制因子。這部分資訊由namenode存儲。
hdfs被設計來跨叢集、跨機器地可靠地存儲海量檔案。它把每個檔案存儲為一系列的block;除了最後一個block,一個檔案的所有block都是相同大小的。為了容錯,一個檔案的block會被複制。對于每個檔案來說,block大小和複制因子都是可配置的。應用程式可以聲明一個檔案的副本數。複制因子可以在檔案建立時聲明,也可以在後來被修改。hdfs上的檔案都是write-once的,并且在任何時刻都是嚴格保證隻有一個writer來寫入。
namenode來控制所有的block的複制決策。它周期性地從叢集中的datanode收集心跳和block報告。收集到心跳則意味着datanode正在提供服務。收集到block報告則包含了一個datanode上的所有block清單。
副本的放置對于hdfs的可靠性和性能來說至關重要。對于副本放置的優化是hdfs差別于其他分布式檔案系統的主要方面。這個特性的得來,需要大量的調優和實踐。rack-aware的副本放置政策的目的是提升資料可靠性、可用性和網絡帶寬使用率。目前對副本放置政策的實作是朝這個方向的第一次努力。短期的目标是在生産系統校驗,學習更多的行為以及建構一個測試和研究複雜政策的基礎。
運作于叢集中的大型hdfs執行個體一般都跨越多個機架(rack)。不同rack之間的兩個節點通訊需要通過交換機。大多數情況下,同一rack内的機器間網絡帶寬要比不同rack之間的大。
通用場景下,當複制因子是3時,hdfs的放置政策是将一個副本放置到本地rack的一個節點上,另一個在本地rack的不同節點上,最後一個放在不同rack的不同節點上。這一政策減少了rack之間的寫操作進而提升了寫性能。rack不可用的機率要比node不可用的機率低很多;這一政策并不影響資料可靠性和可用性。但是這一政策也确實減少了讀取資料時的聚合網絡帶寬,畢竟一個block 資料是放置在兩個不同的rack下而不是三個。這一政策沒有均勻地分布副本。其中三分之一的副本在一個節點上,三分之二的副本在一個rack上,另三分之一的副本均勻分布在其他rack上。這一政策提高了寫入性能而沒有影響資料可靠性或者讀取性能。
這裡所描述的目前的預設副本放置政策是一項正在進行的工作。
為了最小化全局帶寬消耗和讀取延遲,hdfs試圖滿足從距離reader最近的副本讀取資料的請求。如果在reader節點相同的rack内有副本存在,則由該副本提供資料滿足讀請求。如果hdfs叢集跨越多個資料中心,那麼本地資料中心的副本要優先于遠端副本。
啟動時,namenode會進入一個特殊的狀态叫做安全模式。當namenode進入安全模式時,資料block不會進行複制。namenode會接收datanode的心跳和block報告消息。block報告包含了datanode所負責的資料block清單。每個block有一個特定的最小副本數。當資料block的最小副本數通過namenode檢查後,一個block才被認為是安全複制。namenode檢查了一定比例的(配置過的)安全複制block後(額外再加30秒),namenode退出安全模式。然後繼續确定那些有比指定副本數還少的資料block清單(如果有的話)。namenode然後複制這些block到其他的datanode。
hdfs的命名空間是存儲在namenode上的。namenode使用一個叫做editlog的事務日志來記錄發生在檔案系統中繼資料上的每一個變更。舉個例子,在hdfs上建立一個新檔案會導緻namenode插入editlog一條新紀錄。類似地,改變一個檔案的複制因子也會導緻插入editlog一條記錄。namenode在其本地作業系統的檔案系統裡用一個檔案來存儲editlog。整個檔案系統的命名空間——包含block和檔案和檔案系統屬性的映射——被存儲在叫做fsimage的檔案裡。fsimage也是namenode本地系統中的一個問價。
namenode在記憶體裡保持了一個包含整個檔案系統命名空間和block映射的檔案鏡像。這個關鍵的中繼資料項是可壓縮的,一個4gb記憶體的namenode可以支援海量的檔案和目錄。當namenode啟動時,它會從磁盤讀取fsimage和editlog,然後将editlog的所有事務應用到記憶體中的fsimage,然後将新的fsimage刷盤回新的fsimage磁盤檔案。然後它會删掉老的editlog并建立一個新的,因為老的事務已經被apply到fsimage。這一過程叫做一個checkpoint。在目前的實作中,checkpoint隻在namenode啟動時發生。周期性的checkpoint在近期的工作計劃中。
datanode在其本地檔案系統中以檔案的形式存儲了hdfs的資料。datanode對于hdfs檔案無概念。它将hdfs資料的每個block存儲到本地檔案系統的獨立的檔案中。datanode不會再同一個目錄下建立所有的檔案。相反,它使用啟發式确定每個目錄中的檔案的最佳數量并适當地建立子目錄。在本地檔案系統中的一個目錄下建立所有的檔案并不是最佳政策,因為本地檔案系統可能并不能高效的支撐單一目錄下的大量檔案存儲。當datanode啟動時,它會掃描本地檔案系統内,生成一個hdfs資料block和本地檔案的映射的清單,然後将此報告發送給namenode:這就是block報告。
所有的hdfs通訊協定都是基于tcp/ip協定之上的。一個用戶端建立一個連接配接,連接配接到namenode機器對應配置的tcp端口。他們基于用戶端協定來與namenode通訊。datanode使用datanode協定與namenode通訊。一個遠端過程調用(rpc)同時抽象封裝了用戶端協定和datanode協定。設計上,namenode從不發起任何的rpc調用。相反,namenode隻響應從datanode或者用戶端發起的rpc請求。
hdfs的首要目标是可靠地存儲資料,就算發生失敗也仍然有效。三種常見類型的失效包括:namenode失效,datanode失效和網絡分區。
每個datanode會周期性地發送心跳資訊給namenode。一次網絡分區會導緻一組datanode與namenode丢失連接配接。namenode會通過心跳缺失而檢測到這次連接配接的丢失。namenode會把這些檢測不到心跳的datanode标記為“死亡”,然後不再發送任何新的io請求到這些datanode。任何已經注冊到“死亡”的datanode上的資料對hdfs不再可用。datanode的死亡可能會導緻某些block的複制因子低于其指定值。namenode持續地追蹤着需要被複制的block,并在其需要時進行複制。重新複制的必要性可能在于以下原因:一個的datanode可能變得不可用,副本可能損壞,一個的datanode硬碟可能失敗,或一個檔案的複制因子可能增加。
hdfs的架構與資料重新均衡方案相容。一種均衡方案是在一個datanode可用空間低于一定門檻值時,會自動将資料從datanode搬運到另一個datanode。在某個特定檔案突然的高需求情況下,一種方案可能動态地建立額外副本,并在叢集中均衡其他資料。這些資料重新均衡的方案都還沒有被實作。
從一個datanode擷取的block資料很可能到達時崩潰。發生這種崩潰是因為儲存設備出錯,網絡出錯或者軟體bug。hdfs用戶端軟體實作了校驗hdfs檔案的checksum的功能。當用戶端建立了一個hdfs檔案,它會為這個檔案的每個block計算一個checksum,并将這些checksum存儲到同名的hdfs命名空間下的一個獨立的隐藏檔案。當用戶端拉取檔案内容時,它會校驗從datanode拉取到的資料與其關聯的checksum檔案的checksum是否一緻。如果不一緻,那麼用戶端可以再嘗試從其他的擁有該block副本的datanode拉取資料。
fsimage和editlog是hdfs的核心資料結構。這些檔案的崩潰會導緻hdfs整個執行個體的不可用。基于此原因,namenode需要被配置為可以支援存儲fsimage和editlog的多份副本。任何對于fsimage或者editlog的改動都會同步更新到每個fsimage和editlog的副本。這種同步更新會降低namenode可支援處理的命名空間事務速率。但是,這種降低是可接受的,因為即使hdfs程式是資料密集的,但是中繼資料不是資料密集的。當一個namenode重新開機時,它會選擇最新的一緻的fsimage和editlog來使用。
namenode機器是hdfs叢集的故障單點。如果namenode機器挂了,那麼人工幹預是必要的。目前情況下,namenode的自動重新開機和failover到其他機器還沒有被支援。
快照支援在特定的一個時刻存儲一份資料的副本。用途之一是用來在hdfs執行個體崩潰時可以復原到之前某個時刻存儲的版本。hdfs目前不支援快照,但是在未來會支援。
hdfs被設計來存儲大檔案呢。與hdfs相容的程式都是處理大型資料集的。這些程式隻進行一次寫資料,但是會多次讀取這些資料,而且是以流式讀取。hdfs支援write-once-read-many的檔案語義。一個hdfs典型的資料塊大小是64 mb。是以hdfs檔案被裁減為很多個64mb大小的塊,如果可能的話,每個塊都分布在不同的datanode上。
用戶端的建立檔案請求部署馬上到達namenode的。事實上,hdfs用戶端會将資料緩存到一個本地臨時檔案裡。應用程式透明地将資料重定向寫入到這個臨時檔案。當本地檔案累積資料達到hdfs塊大小,用戶端才會連接配接namenode。namenode然後會将檔案名寫入檔案系統命名空間并配置設定一個資料塊來存儲這些資料。namenode會傳回給用戶端一個響應,其中包含datanode的id和目标資料塊的id。然後用戶端将資料塊從本地臨時檔案重新整理到對應datanode。當一個檔案關閉後,本地臨時檔案中剩餘的未重新整理的資料會被傳輸到datanode。然後用戶端會通知namenode檔案關閉。此時此刻,namenode會送出一個建立檔案的操作到持久存儲。如果namenode在檔案關閉前崩潰了,那麼檔案就丢失了。
在認真評估考慮hdfs的目标程式後,上面提到的方法被認可并接受。這些程式需要流式地寫檔案。一個用戶端不做任何用戶端buffer直接寫遠端檔案的話,網絡速度和帶寬會影響吞吐。這種方法并非沒有先例。之前的分布式檔案系統比如afs就曾經在用戶端側添加緩存來提升性能。posix規範已經被放寬進而可以擷取更高的資料上傳性能。
當用戶端正在往hdfs檔案中寫資料時,資料首先會寫入一個本地檔案(如之前所述)。假設hdfs檔案的複制因子是3。當本地檔案積累使用者資料滿足一個完整block時,用戶端從namenode拉取一個datanode的清單。這個清單包含了可以承載該資料塊一個資料副本的datanode。用戶端然後将資料塊重新整理到第一個datanode。第一個datanode接着開始小部分地接收資料,然後将每部分寫入到它的本地庫,并且将該部分資料傳輸到清單中第二個datanode。第二個datanode開始接收該資料塊的資料,并将該部分資料寫入它的本地庫然後将該部分資料重新整理到第三個datanode。最終,第三個datanode将資料寫入其本地庫。這樣,一個datanode可以從前一個datanode接收資料,以流水線的方式在同一時刻将資料轉發到流水線中的下一個datanode。是以資料是以流水線的方式從一個datanode傳輸到下一個。
hdfs可以被應用程式以多種方式來通路。hdfs提供給應用程式一種原生的檔案系統java api。一個基于此java api的c語言封裝也是可用的。額外的情況下,http浏覽器也可以用來浏覽hdfs執行個體的檔案。通過webdav協定暴露hdfs的工作仍在進行中。
hdfs允許使用者資料以檔案和目錄的形式組織。它提供了一個叫做fs shell的指令行界面來作為使用者與hdfs資料互動的中介。該指令行的指令文法與其他的大家廣為熟悉的檔案系統shell類似(比如bash和csh)。這裡列出一些行為和指令的對應:
action
command
建立個叫做/foodir的目錄
bin/hadoop dfs -mkdir /foodir
删除/foodir的目錄
bin/hadoop dfs -rmr /foodir
檢視/foodir/myfile.txt檔案的内容
bin/hadoop dfs -cat /foodir/myfile.txt
fs shell是給那些需要用腳本語言與資料互動的應用程式使用的。
dfsadmin指令用于管理一個hdfs叢集。這些指令隻可以由hdfs管理者使用。這裡列出一些行為和指令的對應:
設定叢集為安全模式
bin/hadoop dfsadmin -safemode enter
生成datanode的清單
bin/hadoop dfsadmin -report
recommission or decommission datanode(s)
bin/hadoop dfsadmin -refreshnodes
一個典型的hdfs安裝會配置一個web server透過一個配置的tcp端口來暴露hdfs命名空間。以此來允許使用者通過使用web浏覽器來浏覽hdfs命名空間和檢視檔案内容。
當一個檔案被使用者或者應用程式删除時,它并沒有立刻被hdfs移除。相反,hdfs會首先将其重命名然後移入一個/trash的目錄。在/trash裡的檔案可以快速的被恢複使用。一個檔案在/trash裡維持的時間由一個配置來決定。當在/trash裡過期後,namenode從hdfs命名空間删除這個檔案。删除檔案會關聯其對應的資料塊的空間釋放。注意在使用者删除檔案和對應的hdfs空間釋放之間有個時間的延遲。
使用者可以取消删除一個檔案,隻要它還在/trash目錄下。如果一個使用者希望取消删除一個已經删除了的檔案,那麼他可以浏覽/trash目錄并找到對應的檔案。/trash目錄隻包含被删除檔案的最細副本。/trash目錄與其他目錄一樣,唯獨的不同在于:hdfs啟用了特殊的政策來自動删除該目錄下的檔案。目前預設的回收間隔設定是0(不在資源回收筒保留,直接删除)。這個參數是可以配置的,配置項在core-site.xml裡的fs.trash.interval。
當一個檔案的複制因子被降低,namenode就可以選擇多餘的可被删除的副本。下一次的心跳資訊會傳輸該資訊給datanode。datanode會删除對應的block,釋放相應的空間。與之前的延遲類似,在setreplication api調用結束後到實際空間釋放之間存在一個延遲。