Hadoop分布式文件系統(tǒng)(HDFS)的設(shè)計(jì)主旨,在于對(duì)超大規(guī)模數(shù)據(jù)集提供可靠的存儲(chǔ)功能,并對(duì)用戶應(yīng)用程序提供高帶寬的輸入輸出數(shù)據(jù)流。在大型的集群里,上千臺(tái)服務(wù)器均可直接參與到數(shù)據(jù)存儲(chǔ)和應(yīng)用程序任務(wù)執(zhí)行。通過多服務(wù)器,分布式的存儲(chǔ)和計(jì)算,計(jì)算資源的規(guī)模能夠按照需要增長(zhǎng),并兼顧在各種規(guī)模上經(jīng)濟(jì)適用性。 本文主要描述了HDFS的架構(gòu),并以Yahoo!企業(yè)數(shù)據(jù)服務(wù)為例,介紹了如何使用HDFS系統(tǒng)管理高達(dá)4O PB規(guī)模的數(shù)據(jù)庫(kù)的經(jīng)驗(yàn)。
8.1 介紹
Hadoop采用MapReduce范式[DG04]進(jìn)行設(shè)計(jì),提供了一套分布式文件系統(tǒng)和框架,用以對(duì)超大規(guī)模的數(shù)據(jù)集進(jìn)行分析和變換。HDFS的接口沿襲了Unix文件系統(tǒng)的設(shè)計(jì)模式,但在其基礎(chǔ)上做出了改進(jìn),以提高在實(shí)際應(yīng)用中的訪問性能。
Hapoop所具有一個(gè)重要特點(diǎn),就是把數(shù)據(jù)和運(yùn)算分開,并將二者分布存放在數(shù)以千計(jì)的服務(wù)器主機(jī)上,應(yīng)用程序計(jì)算以及相關(guān)數(shù)據(jù)都以并行方式處理。一個(gè)Hadoop集群可以僅僅通過增加普通服務(wù)器的方式,來擴(kuò)展其運(yùn)算、存儲(chǔ)和I/O帶寬的規(guī)模。Yahoo所使用的Hadoop集群組,共包含40,000臺(tái)服務(wù)器,存儲(chǔ)并處理多達(dá)40 PB(1 Petabytes = 1000000000000000 字節(jié))的應(yīng)用數(shù)據(jù),其中最大的單個(gè)集群,使用多達(dá)4000個(gè)服務(wù)器。此外,在世界范圍內(nèi),還有100多家其他的組織和機(jī)構(gòu)表示,他們也使用Hadoop來進(jìn)行數(shù)據(jù)存儲(chǔ)和處理。
HDFS將文件系統(tǒng)元數(shù)據(jù)(File System Metadata)和應(yīng)用數(shù)據(jù)(Application Data)分離存放。與其他種類的分布式文件系統(tǒng)類似,例如PVFS[CIRT00], Lustre2, 以及GFS[GGL03],HDFS將元數(shù)據(jù)存放在專用服務(wù)器上,該服務(wù)器稱為“NameNode”(名稱節(jié)點(diǎn));應(yīng)用數(shù)據(jù)被存放在其他的服務(wù)器上,這些服務(wù)器稱為“DataNode”(數(shù)據(jù)節(jié)點(diǎn))。在該分布式系統(tǒng)中,各個(gè)服務(wù)器之間均通過網(wǎng)絡(luò)連接,確保節(jié)點(diǎn)之間可以通過基于TCP族的協(xié)議進(jìn)行相互通信。HDFS不像Lustre或者PVFS,它并不依賴于數(shù)據(jù)保護(hù)機(jī)制(例如RAID)來確保數(shù)據(jù)的穩(wěn)定性,而是像GFS那樣,在多個(gè)DataNode節(jié)點(diǎn)上保存數(shù)據(jù)的多個(gè)副本,以此來確保數(shù)據(jù)的穩(wěn)定。采用這樣的策略,其好處不僅僅在于數(shù)據(jù)安全方面,在數(shù)據(jù)傳輸帶寬方面,由于一個(gè)數(shù)據(jù)有多個(gè)副本,因此可以通過多線程訪問倍速提高帶寬(就像迅雷下載的原理一樣——譯者注),并且采用此種方式還可以提高從較近的服務(wù)器節(jié)點(diǎn)上獲取數(shù)據(jù)的幾率。
8.2 架構(gòu)
8.2.1 NameNode(名稱節(jié)點(diǎn))
HDFS命名空間采用層次化(樹狀——譯者注)的結(jié)構(gòu)存放文件和目錄。文件和目錄用NameNode上的inodes表示。Inode記錄了權(quán)限,修改和訪問時(shí)間,命名空間,磁盤容量等屬性。文件內(nèi)容會(huì)被分成不同的“大塊”(典型分塊策略是每塊128M,不過用戶可以對(duì)每個(gè)文件的分塊大小進(jìn)行選擇)。NameNode負(fù)責(zé)維護(hù)命名空間樹以及與DataNode上文件分塊的映射關(guān)系。目前采用的設(shè)計(jì)結(jié)構(gòu)是,沒一個(gè)集群只有一個(gè)NameNode,一個(gè)NameNode可以對(duì)應(yīng)多個(gè)DataNode以及成千上萬的HDFS客戶端。一個(gè)DataNode可以同步執(zhí)行多個(gè)應(yīng)用任務(wù)。
8.2.2 映像和日志
Inode和定義metadata的系統(tǒng)文件塊列表統(tǒng)稱為Image(映像). NameNode將整個(gè)命名空間映像保存在RAM中。而映像的持久化記錄則保存在NameNode的本地文件系統(tǒng)中,該持久化記錄被稱為Checkpoint(檢查點(diǎn))。NameNode還會(huì)記錄HDFS中寫入的操作,并將其存入一個(gè)記錄文件,存放在本地文件系統(tǒng)中,這個(gè)記錄文件被叫做Journal(日志)。存放塊位置的副本不屬于持久化檢查點(diǎn)(persistent checkpoint)的一個(gè)部分。 每個(gè)客戶端發(fā)起的事務(wù)都會(huì)被記錄到日志里,然后日志文件會(huì)被刷新和同步,再發(fā)送回客戶端。NameNode上的檢查點(diǎn)文件(Checkpoint file)一旦生成,就不允許再修改。如果NameNode重啟,在系統(tǒng)管理員的要求下,或者根據(jù)CheckpointNode的定義(下章介紹),可以生成一個(gè)新的文件記錄checkpoint。 在啟動(dòng)過程中,NameNode會(huì)從checkpoint中初始化命名空間映像,然后根據(jù)日志重現(xiàn)所有的寫入更改操作。在NameNode開始響應(yīng)客戶端之前,一個(gè)新的checkpoint和一個(gè)空的日志將被保存到存儲(chǔ)目錄當(dāng)中。
為了提高持久性,系統(tǒng)會(huì)將checkpoint文件和日志的多個(gè)冗余備份存儲(chǔ)到多個(gè)獨(dú)立的本地卷以及遠(yuǎn)程N(yùn)FS服務(wù)器上。之所以存儲(chǔ)到獨(dú)立卷標(biāo),是為了避免單個(gè)卷標(biāo)失效后造成文件丟失;存儲(chǔ)到遠(yuǎn)程服務(wù)器則是為了預(yù)防整個(gè)節(jié)點(diǎn)崩潰后造成所有本地文件丟失。如果NameNode遇到了錯(cuò)誤,無法將日志信息寫入到某個(gè)存儲(chǔ)目錄,那么系統(tǒng)就會(huì)自動(dòng)將該有問題的目錄排除到存儲(chǔ)目錄列表的范圍之外。如果NameNode發(fā)現(xiàn)連一個(gè)可用的存儲(chǔ)目錄都找不到,則會(huì)執(zhí)行自動(dòng)關(guān)閉操作(節(jié)點(diǎn)失效)。
NameNode是一個(gè)多線程的系統(tǒng)應(yīng)用,可以同時(shí)處理多個(gè)客戶端的申請(qǐng)。不過,將事務(wù)存儲(chǔ)到磁盤是一個(gè)較大的性能瓶頸,因?yàn)槿绻幸粋€(gè)線程正在存儲(chǔ)中,其他線程都必須等待該線程完成其刷新和同步過程完成后,才能繼續(xù)進(jìn)行操作。為了優(yōu)化這一過程,NameNode采用將多個(gè)事務(wù)批處理的方式,當(dāng)某個(gè)NameNode線程初始化了一個(gè)刷新同步操作時(shí),所有的事務(wù)會(huì)被一次性批處理,然后一起提交。其他的線程只需要檢查他們的事務(wù)是否被存儲(chǔ)了即可,而不需要再去提交刷新同步操作。
8.2.3 數(shù)據(jù)節(jié)點(diǎn)
DataNode上的每一個(gè)塊(block)副本都由兩個(gè)本地文件系統(tǒng)上的文件共同表示。其中一個(gè)文件包含了塊(block)本身所需包含的數(shù)據(jù),另一個(gè)文件則記錄了該塊的元數(shù)據(jù),包括塊所含數(shù)據(jù)大小和文件生成時(shí)間戳。數(shù)據(jù)文件的大小等于該塊(block)的真實(shí)大小,而不是像傳統(tǒng)的文件系統(tǒng)一樣,需要用額外的存儲(chǔ)空間湊成完整的塊。因此,如果一個(gè)塊里只需要一半的空間存儲(chǔ)數(shù)據(jù),那么就只需要在本地系統(tǒng)上分配半塊的存儲(chǔ)空間即可。
在啟動(dòng)過程中,每個(gè)DataNode通過“握手”的方式與另外一個(gè)NameNode節(jié)點(diǎn)連接。之所以采用“握手”方式,是為了驗(yàn)證DataNode的命名空間ID以及軟件的版本號(hào)。如果一個(gè)節(jié)點(diǎn)的ID或者版本號(hào)不匹配,那么DataNode節(jié)點(diǎn)就會(huì)自動(dòng)關(guān)閉。
命名空間ID是在文件系統(tǒng)實(shí)例格式化的時(shí)候就分配好的。命名空間ID被在集群內(nèi)的所有節(jié)點(diǎn)上都有持久化存儲(chǔ)。由于不同命名空間ID的節(jié)點(diǎn)無法加入到集群中,因此能夠保證集群文件系統(tǒng)的統(tǒng)一性。一個(gè)DataNode在剛初始化的時(shí)候沒有命名空間ID,此時(shí)該節(jié)點(diǎn)被允許加入集群,一旦加入,該節(jié)點(diǎn)就會(huì)以加入的集群的ID作為自己的ID。
“握手”之后,DataNode被注冊(cè)到NameNode。DataNode持久化保存其唯一的存儲(chǔ)ID(storage ID)。存儲(chǔ)ID是一個(gè)DataNode的內(nèi)部標(biāo)識(shí)符,該標(biāo)識(shí)符能夠確保即使是服務(wù)器用不同的IP地址或者端口啟動(dòng),仍然可以被識(shí)別。存儲(chǔ)ID在DataNode首次注冊(cè)到NameNode時(shí)即被分配,一旦分配后便無法更改。
DataNode采用發(fā)送“塊報(bào)告”(block report)的形式,向NameNode標(biāo)識(shí)其所包含的塊副本。塊報(bào)告包含了塊ID,生成時(shí)間戳,以及每個(gè)塊副本的長(zhǎng)度等等。 首個(gè)塊報(bào)告會(huì)在DataNode注冊(cè)后立即發(fā)送。隨后的塊報(bào)告會(huì)每小時(shí)發(fā)送一次,以確保NameNode能夠知道集群中塊副本的最新情況。
在正常情況下,DataNode想NameNode發(fā)送“心跳信號(hào)”,以確認(rèn)DataNode運(yùn)行正常,以及其所包含的塊數(shù)據(jù)可用。默認(rèn)的“心跳信號(hào)”的時(shí)間間隔是3秒。如果NameNode長(zhǎng)達(dá)10分鐘沒有接受到來自于DataNode的心跳信號(hào),那么久會(huì)認(rèn)為為該DataNode節(jié)點(diǎn)已經(jīng)失效,其所包含的塊(block)已經(jīng)無法使用。接下來NameNode就會(huì)計(jì)劃在其他的DataNode上創(chuàng)建新的塊數(shù)據(jù)。
來自DataNode的心跳信號(hào)也會(huì)附帶包括總存儲(chǔ)容量,存儲(chǔ)使用量,當(dāng)前數(shù)據(jù)傳輸進(jìn)度等附加信息。這些統(tǒng)計(jì)數(shù)據(jù)可用于NameNode塊分配,以及作為負(fù)載均衡決策的參考。
NameNode不能直接向DataNode發(fā)送請(qǐng)求。它只通過回復(fù)心跳信號(hào)的方式來向DataNode發(fā)送指令。指令的內(nèi)容包括,將塊移到其他節(jié)點(diǎn),移除本地塊副本,重新注冊(cè)和發(fā)送即時(shí)塊報(bào)告,關(guān)閉當(dāng)前節(jié)點(diǎn)等等。
這些命令對(duì)于維護(hù)整個(gè)系統(tǒng)的完整性來說非常關(guān)鍵,因此即使是在超大集群上,保持心跳信號(hào)的頻率也是至關(guān)重要的。NameNode每秒能夠處理上千條心跳信號(hào),并且不影響到NameNode的其他正常操作。
8.2.4 HDFS客戶端
用戶應(yīng)用程序通過HDFS客戶端連接到HDFS文件系統(tǒng),通過庫(kù)文件可導(dǎo)出HDFS文件系統(tǒng)的接口。
像很多傳統(tǒng)的文件系統(tǒng)一樣,HDFS支持文件的讀、寫和刪除操作,還支持對(duì)目錄的創(chuàng)建和刪除操作。用戶通過帶命名空間的路徑對(duì)文件和目錄進(jìn)行引用。用戶程序不需要知道文件系統(tǒng)的元數(shù)據(jù)和具體存儲(chǔ)在哪個(gè)服務(wù)器上,也不需要關(guān)心一個(gè)塊有多少個(gè)副本。
當(dāng)一個(gè)應(yīng)用程序讀一個(gè)文件的時(shí)候,HDFS客戶端首先向NameNode索要包含該文件的文件塊的DataNode節(jié)點(diǎn)的列表。該列表會(huì)按照網(wǎng)絡(luò)拓?fù)渚嚯x的遠(yuǎn)近進(jìn)行排序。然后客戶端會(huì)直接與相應(yīng)的DataNode節(jié)點(diǎn)進(jìn)行聯(lián)系,要求傳輸所需的文件塊。當(dāng)客戶端寫一個(gè)文件的時(shí)候,它會(huì)首先要求NameNode選擇一個(gè)DataNode,該DataNode需要包含所寫入的文件的首個(gè)文件塊。接下來,客戶端會(huì)搭建一個(gè)從節(jié)點(diǎn)到節(jié)點(diǎn)的通信管道,用以進(jìn)行數(shù)據(jù)傳輸。當(dāng)?shù)谝粋€(gè)塊被寫入后,客戶端會(huì)申新的DataNode,用以寫入下一個(gè)塊。此時(shí),新的通信管線建立,客戶端會(huì)通過管線寫入更多的數(shù)據(jù)。每個(gè)文件塊所寫入的DataNode節(jié)點(diǎn)也許會(huì)完全不同。客戶端,NameNode和DataNode之間的關(guān)系如圖8.1所示。
與傳統(tǒng)的文件系統(tǒng)不同的是,HDFS提供一個(gè)API用以暴露文件塊的位置。這個(gè)功能允許應(yīng)用程序,例如MapReduce框架,去數(shù)據(jù)所存放的地點(diǎn)進(jìn)行任務(wù)調(diào)度,以此來提高讀數(shù)據(jù)的新能。API也允許一個(gè)應(yīng)用程序設(shè)定文件的復(fù)制因子。默認(rèn)情況下,文件的復(fù)制因子是3,。對(duì)于關(guān)鍵的文件或者使用頻率較多的文件,使用更高的復(fù)制因子,能夠提高容錯(cuò)性,以及提升文件的訪問帶寬。
8.2.5 檢查點(diǎn)節(jié)點(diǎn)
HDFS中的NameNode節(jié)點(diǎn),除了其主要職責(zé)是相應(yīng)客戶端請(qǐng)求以外,還能夠有選擇地扮演一到兩個(gè)其他的角色,例如做檢查點(diǎn)節(jié)點(diǎn)或者備份節(jié)點(diǎn)。該角色是在節(jié)點(diǎn)啟動(dòng)的時(shí)候特有的。
檢查點(diǎn)節(jié)點(diǎn)定期地域已經(jīng)存在的檢查點(diǎn)和日志一起,創(chuàng)建新的檢查點(diǎn)和空日志。檢查點(diǎn)節(jié)點(diǎn)通常運(yùn)行于一個(gè)非NameNode節(jié)點(diǎn)的主機(jī)上,但它和NameNode節(jié)點(diǎn)擁有相同的內(nèi)存需求配置。檢查點(diǎn)節(jié)點(diǎn)從NameNode上下載當(dāng)前的檢查點(diǎn)和日志文件,將其在本地進(jìn)行合并,并將新的檢查點(diǎn)返回到NameNode.
創(chuàng)建一個(gè)定期檢查點(diǎn)是保護(hù)文件系統(tǒng)元數(shù)據(jù)的一種方式。如果命名空間映像中的所有其他持久化拷貝均無法使用,系統(tǒng)還能夠從最近一次的checkpoint文件中啟動(dòng)。當(dāng)創(chuàng)建一個(gè)新的checkpoint被更新到NameNode的時(shí)候,還能讓NameNode產(chǎn)生截?cái)嗳罩镜男Ч?。HDFS集群組可以長(zhǎng)時(shí)間持續(xù)運(yùn)行,無需重啟,但這也導(dǎo)致了系統(tǒng)日志的大小會(huì)不斷增長(zhǎng)。當(dāng)系統(tǒng)日志大到一定程度的時(shí)候,日志文件丟失或者損壞的幾率就會(huì)增加。所以,一個(gè)日志太大的節(jié)點(diǎn)需要重啟一下來對(duì)日志文件進(jìn)行更新(截?cái)啵?。?duì)于一個(gè)較大的集群來說,平均處理一周的日志內(nèi)容需要耗費(fèi)一小時(shí)的時(shí)間。所以較好的頻率是,每天創(chuàng)建一次新日志。
8.2.6 備份節(jié)點(diǎn)
HDFS的備份節(jié)點(diǎn)是最近在加入系統(tǒng)的一項(xiàng)特色功能。就像CheckpintNode一樣,備份節(jié)點(diǎn)能夠定期創(chuàng)建檢查點(diǎn),但是不同的是,備份節(jié)點(diǎn)一直保存在內(nèi)存中,隨著文件系統(tǒng)命名空間的映像更新和不斷更新,并與NameNode的狀態(tài)隨時(shí)保持同步。
備份節(jié)點(diǎn)從活動(dòng)的NameNode節(jié)點(diǎn)中接受命名空間事務(wù)的日志流,并將它們以日志的形式存儲(chǔ)在其自身所帶的存儲(chǔ)目錄里,并使用自身的內(nèi)存和命名空間映像來執(zhí)行這個(gè)事務(wù)。NameNodez則將BackupNode當(dāng)做日志一樣看待,就仿佛是存儲(chǔ)在其自身的存儲(chǔ)目錄里一樣。如果NameNode失效,那么BackupNode節(jié)點(diǎn)內(nèi)存中的映像和磁盤上的checkpoint文件就可以作為最近的命名空間狀態(tài)的備份,以備還原。
BackupNode能夠動(dòng)態(tài)創(chuàng)建一個(gè)checkpoint,而不需要從活動(dòng)的NameNode上下載其checkpoint文件和日志文件。因?yàn)锽ackupNode始終把最新的狀態(tài)保存在它自身命名空間的內(nèi)存中。這一特性使得在BackupNode節(jié)點(diǎn)上處理checkpoint變得非常高效,因?yàn)橹恍枰衙臻g存儲(chǔ)到本地服務(wù)器就可以了,而不需要和NameNode再進(jìn)行交互。
BackupNode還可以被看做是一個(gè)只讀的NameNode. 它包含了除文件塊位置以外的所有文件系統(tǒng)的元數(shù)據(jù)信息。除了修改命名空間或者文件塊位置以外,BackupNode可以做NameNode所能做的所有操作。使用BackupNode能夠使NameNode在運(yùn)行的時(shí)候不進(jìn)行持久化存儲(chǔ),從而把持久化命名空間狀態(tài)的任務(wù)挪到BackupNode節(jié)點(diǎn)上進(jìn)行。
8.2.7 系統(tǒng)更新和文件系統(tǒng)快照
在軟件更新的過程中,由于軟件的bug或者人為操作的失誤,文件系統(tǒng)損壞的幾率會(huì)隨之提升。在HDFS中創(chuàng)建系統(tǒng)快照的目的,就在于把系統(tǒng)升級(jí)過程中可能對(duì)數(shù)據(jù)造成的隱患降到最低。
快照機(jī)制讓系統(tǒng)管理員將當(dāng)前系統(tǒng)狀態(tài)持久化到文件系統(tǒng)中,這樣以來,如果系統(tǒng)升級(jí)后出現(xiàn)了數(shù)據(jù)丟失或者損壞,便有機(jī)會(huì)進(jìn)行回滾操作,將HDFS的命名空間和存儲(chǔ)狀態(tài)恢復(fù)到系統(tǒng)快照進(jìn)行的時(shí)刻。
系統(tǒng)快照(只能有一個(gè))在系統(tǒng)啟動(dòng)后,根據(jù)集群管理員的選擇可隨時(shí)生成。如果要求生成一個(gè)系統(tǒng)快照,NameNode首先會(huì)讀取checkpoint和日志文件,并將其在內(nèi)存中合并。然后,NameNode在新的位置創(chuàng)建并寫入一個(gè)新的checkpoint和空的日志,保證舊的Checkpoint和日志不會(huì)被改變。
在進(jìn)行“握手”方式通信時(shí),NameNode指示DataNode是否要?jiǎng)?chuàng)建一個(gè)本地的系統(tǒng)快照。DataNode上的本地系統(tǒng)快照不會(huì)復(fù)制本地的存儲(chǔ)目錄中包含的數(shù)據(jù)信息,因?yàn)槿绻@么做的話,會(huì)使得每個(gè)集群上的DataNode上的數(shù)據(jù)占據(jù)雙倍的存儲(chǔ)空間。因此,取而代之的策略是,建立一份目錄結(jié)構(gòu)的副本,并用“鏈接”的方式將已經(jīng)存在的塊文件指向到目錄副本。當(dāng)DataNode嘗試移除一個(gè)文件塊時(shí),只需要移除鏈接就可以了,在文件塊增量時(shí),也是使用copy-on-wirte技術(shù)。所以舊的塊副本在其原先的目錄結(jié)構(gòu)中保持不變。
集群管理員可以選擇在重啟系統(tǒng)時(shí),將HDFS回滾到快照所表示的狀態(tài)。NameNode在快照恢復(fù)時(shí),會(huì)恢復(fù)所記錄的checkpoint。DataNode則會(huì)恢復(fù)之前被更名的文件目錄,并初始化一個(gè)幕后進(jìn)程,用以刪除在snapshot創(chuàng)建之后系統(tǒng)新增的文件塊副本。選擇回滾后,回滾之前的操作將無法再繼續(xù)(無法前滾)。集群管理員可以下達(dá)放棄快照的指令,來恢復(fù)被快照功能所占用的存儲(chǔ)權(quán)。如果再系統(tǒng)升級(jí)期間進(jìn)行快照,那么升級(jí)過程將被終止。
隨著系統(tǒng)的升級(jí),NameNode的checkpoint文件和日志文件的格式可能會(huì)發(fā)生變化,或者塊文件的數(shù)據(jù)結(jié)構(gòu)也可能發(fā)生改變。因此,系統(tǒng)中使用“設(shè)計(jì)版本號(hào)”(layout version)來標(biāo)識(shí)數(shù)據(jù)表現(xiàn)格式,該版本號(hào)被持久化地存儲(chǔ)在NameNode以及DataNode的存儲(chǔ)目錄中。每個(gè)節(jié)點(diǎn)在啟動(dòng)時(shí),會(huì)將其系統(tǒng)設(shè)計(jì)版本號(hào)和存儲(chǔ)目錄中的設(shè)計(jì)版本號(hào)進(jìn)行對(duì)比,并自動(dòng)嘗試將舊的數(shù)據(jù)格式轉(zhuǎn)換到新的版本。為了實(shí)現(xiàn)系統(tǒng)版本升級(jí),新版本重啟時(shí)會(huì)強(qiáng)制創(chuàng)建系統(tǒng)快照。
8.3 文件 I/O 操作和復(fù)制管理
當(dāng)然,一個(gè)文件系統(tǒng)的根本任務(wù)是用來存儲(chǔ)數(shù)據(jù)和文件。如果要理解HDFS如何完成這一基本任務(wù),我們就必須從它如何進(jìn)行數(shù)據(jù)的讀寫,以及文件塊如何管理這兩方面來說明。
8.3.1 文件讀寫
應(yīng)用程序通過創(chuàng)建新文件以及向新文件寫數(shù)據(jù)的方式,給HDFS系統(tǒng)添加數(shù)據(jù)。文件關(guān)閉以后,被寫入的數(shù)據(jù)就無法再修改或者刪除,只有以“追加”方式重新打開文件后,才能再次為文件添加數(shù)據(jù)。HDFS采用單線程寫,多線程讀的模式。
HDFS客戶端需要首先獲得對(duì)文件操作的授權(quán),然后才能對(duì)文件進(jìn)行寫操作。在此期間,其他的客戶端都不能對(duì)該文件進(jìn)行寫操作。被授權(quán)的客戶端通過向NameNode發(fā)送心跳信號(hào)來定期更新授權(quán)的狀態(tài)。當(dāng)文件關(guān)閉時(shí),授權(quán)會(huì)被回收。文件授權(quán)期限分為軟限制期和硬限制期兩個(gè)等級(jí)。當(dāng)處于軟限制期內(nèi)時(shí),寫文件的客戶端獨(dú)占對(duì)文件的訪問權(quán)。當(dāng)軟限制過期后,如果客戶端無法關(guān)閉文件,或沒有釋放對(duì)文件的授權(quán),其他客戶端即可以預(yù)定獲取授權(quán)。當(dāng)硬限制期過期后(一小時(shí)左右),如果此時(shí)客戶端還沒有更新(釋放)授權(quán),HDFS會(huì)認(rèn)為原客戶端已經(jīng)退出,并自動(dòng)終止文件的寫行為,收回文件控制授權(quán)。文件的寫控制授權(quán)并不會(huì)阻止其他客戶端對(duì)文件進(jìn)行讀操作。因此一個(gè)文件可以有多個(gè)并行的客戶端對(duì)其進(jìn)行讀取。
HDFS文件由多個(gè)文件塊組成。當(dāng)需要?jiǎng)?chuàng)建一個(gè)新文件塊時(shí),NameNode會(huì)生成唯一的塊ID,分配塊空間,以及決定將塊和塊的備份副本存儲(chǔ)到哪些DataNode節(jié)點(diǎn)上。DataNode節(jié)點(diǎn)會(huì)形成一個(gè)管道,管道中DataNode節(jié)點(diǎn)的順序能夠確保從客戶端到上一DataNode節(jié)點(diǎn)的總體網(wǎng)絡(luò)距離最小。文件的則以有序包(sequence of packets)的形式被推送到管道。應(yīng)用程序客戶端創(chuàng)建第一個(gè)緩沖區(qū),并向其中寫入字節(jié)。第一個(gè)緩沖區(qū)被填滿后(一般是64 KB大小),數(shù)據(jù)會(huì)被推送到管道。后續(xù)的包隨時(shí)可以推送,并不需要等前一個(gè)包發(fā)送成功并發(fā)回通知(這被稱為“未答復(fù)發(fā)送”——譯者注)。不過,這種未答復(fù)發(fā)送包的數(shù)目會(huì)根據(jù)客戶端所限定的“未答復(fù)包窗口”(outstanding packets windows)的大小進(jìn)行限制。
在數(shù)據(jù)寫入HDFS文件后,只要文件寫操作沒有關(guān)閉,HDFS就不保證數(shù)據(jù)在此期間對(duì)新增的客戶端讀操作可見。如果客戶端用戶程序需要確保對(duì)寫入數(shù)據(jù)的可見性,可以顯示地執(zhí)行hflush操作。這樣,當(dāng)前的包就會(huì)被立即推送到管道,并且hflush操作會(huì)一直等到所有管道中的DataNode返回成功接收到數(shù)據(jù)的通知后才會(huì)停止。如此就可以保證所有在執(zhí)行hflush之前所寫入的數(shù)據(jù)對(duì)試圖讀取的客戶端用戶均可見。
更多建議: