Hadoop集群系列9:HDFS初探之旅
1、HDFS簡(jiǎn)介
HDFS(Hadoop Distributed File System)是Hadoop項(xiàng)目的核心子項(xiàng)目,是分布式計(jì)算中數(shù)據(jù)存儲(chǔ)管理的基礎(chǔ),是基于流數(shù)據(jù)模式訪問(wèn)和處理超大文件的需求而開發(fā)的,可以運(yùn)行于廉價(jià)的商用服務(wù)器上。它所具有的高容錯(cuò)、高可靠性、高可擴(kuò)展性、高獲得性、高吞吐率等特征為海量數(shù)據(jù)提供了不怕故障的存儲(chǔ),為超大數(shù)據(jù)集(Large Data Set)的應(yīng)用處理帶來(lái)了很多便利。
Hadoop整合了眾多文件系統(tǒng),在其中有一個(gè)綜合性的文件系統(tǒng)抽象,它提供了文件系統(tǒng)實(shí)現(xiàn)的各類接口,HDFS只是這個(gè)抽象文件系統(tǒng)的一個(gè)實(shí)例。提供了一個(gè)高層的文件系統(tǒng)抽象類org.apache.hadoop.fs.FileSystem,這個(gè)抽象類展示了一個(gè)分布式文件系統(tǒng),并有幾個(gè)具體實(shí)現(xiàn),如下表1-1所示。
表1-1 Hadoop的文件系統(tǒng)
文件系統(tǒng) |
URI方案 |
Java實(shí)現(xiàn) (org.apache.hadoop) |
定義 |
Local |
file |
fs.LocalFileSystem |
支持有客戶端校驗(yàn)和本地文件系統(tǒng)。帶有校驗(yàn)和的本地系統(tǒng)文件在fs.RawLocalFileSystem中實(shí)現(xiàn)。 |
HDFS |
hdfs |
hdfs.DistributionFileSystem |
Hadoop的分布式文件系統(tǒng)。 |
HFTP |
hftp |
hdfs.HftpFileSystem |
支持通過(guò)HTTP方式以只讀的方式訪問(wèn)HDFS,distcp經(jīng)常用在不同的HDFS集群間復(fù)制數(shù)據(jù)。 |
HSFTP |
hsftp |
hdfs.HsftpFileSystem |
支持通過(guò)HTTPS方式以只讀的方式訪問(wèn)HDFS。 |
HAR |
har |
fs.HarFileSystem |
構(gòu)建在Hadoop文件系統(tǒng)之上,對(duì)文件進(jìn)行歸檔。Hadoop歸檔文件主要用來(lái)減少NameNode的內(nèi)存使用。 |
KFS |
kfs |
fs.kfs.KosmosFileSystem |
Cloudstore(其前身是Kosmos文件系統(tǒng))文件系統(tǒng)是類似于HDFS和Google的GFS文件系統(tǒng),使用C++編寫。 |
FTP |
ftp |
fs.ftp.FtpFileSystem |
由FTP服務(wù)器支持的文件系統(tǒng)。 |
S3(本地) |
s3n |
fs.s3native.NativeS3FileSystem |
基于Amazon S3的文件系統(tǒng)。 |
S3(基于塊) |
s3 |
fs.s3.NativeS3FileSystem |
基于Amazon S3的文件系統(tǒng),以塊格式存儲(chǔ)解決了S3的5GB文件大小的限制。 |
Hadoop提供了許多文件系統(tǒng)的接口,用戶可以使用URI方案選取合適的文件系統(tǒng)來(lái)實(shí)現(xiàn)交互。
2、HDFS基礎(chǔ)概念
2.1 數(shù)據(jù)塊(block)
HDFS(Hadoop Distributed File System)默認(rèn)的最基本的存儲(chǔ)單位是64M的數(shù)據(jù)塊。
和普通文件系統(tǒng)相同的是,HDFS中的文件是被分成64M一塊的數(shù)據(jù)塊存儲(chǔ)的。
不同于普通文件系統(tǒng)的是,HDFS中,如果一個(gè)文件小于一個(gè)數(shù)據(jù)塊的大小,并不占用整個(gè)數(shù)據(jù)塊存儲(chǔ)空間。
2.2 NameNode和DataNode
HDFS體系結(jié)構(gòu)中有兩類節(jié)點(diǎn),一類是NameNode,又叫"元數(shù)據(jù)節(jié)點(diǎn)";另一類是DataNode,又叫"數(shù)據(jù)節(jié)點(diǎn)"。這兩類節(jié)點(diǎn)分別承擔(dān)Master和Worker具體任務(wù)的執(zhí)行節(jié)點(diǎn)。
1)元數(shù)據(jù)節(jié)點(diǎn)用來(lái)管理文件系統(tǒng)的命名空間
其將所有的文件和文件夾的元數(shù)據(jù)保存在一個(gè)文件系統(tǒng)樹中。
這些信息也會(huì)在硬盤上保存成以下文件:命名空間鏡像(namespace image)及修改日志(edit log)
其還保存了一個(gè)文件包括哪些數(shù)據(jù)塊,分布在哪些數(shù)據(jù)節(jié)點(diǎn)上。然而這些信息并不存儲(chǔ)在硬盤上,而是在系統(tǒng)啟動(dòng)的時(shí)候從數(shù)據(jù)節(jié)點(diǎn)收集而成的。
2)數(shù)據(jù)節(jié)點(diǎn)是文件系統(tǒng)中真正存儲(chǔ)數(shù)據(jù)的地方。
客戶端(client)或者元數(shù)據(jù)信息(namenode)可以向數(shù)據(jù)節(jié)點(diǎn)請(qǐng)求寫入或者讀出數(shù)據(jù)塊。
其周期性的向元數(shù)據(jù)節(jié)點(diǎn)回報(bào)其存儲(chǔ)的數(shù)據(jù)塊信息。
3)從元數(shù)據(jù)節(jié)點(diǎn)(secondary namenode)
從元數(shù)據(jù)節(jié)點(diǎn)并不是元數(shù)據(jù)節(jié)點(diǎn)出現(xiàn)問(wèn)題時(shí)候的備用節(jié)點(diǎn),它和元數(shù)據(jù)節(jié)點(diǎn)負(fù)責(zé)不同的事情。
其主要功能就是周期性將元數(shù)據(jù)節(jié)點(diǎn)的命名空間鏡像文件和修改日志合并,以防日志文件過(guò)大。這點(diǎn)在下面會(huì)相信敘述。
合并過(guò)后的命名空間鏡像文件也在從元數(shù)據(jù)節(jié)點(diǎn)保存了一份,以防元數(shù)據(jù)節(jié)點(diǎn)失敗的時(shí)候,可以恢復(fù)。
2.3 元數(shù)據(jù)節(jié)點(diǎn)目錄結(jié)構(gòu)
VERSION文件是java properties文件,保存了HDFS的版本號(hào)。
layoutVersion是一個(gè)負(fù)整數(shù),保存了HDFS的持續(xù)化在硬盤上的數(shù)據(jù)結(jié)構(gòu)的格式版本號(hào)。
namespaceID是文件系統(tǒng)的唯一標(biāo)識(shí)符,是在文件系統(tǒng)初次格式化時(shí)生成的。
cTime此處為0
storageType表示此文件夾中保存的是元數(shù)據(jù)節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)。
namespaceID=1232737062
cTime=0
storageType=NAME_NODE
layoutVersion=-18
2.4 數(shù)據(jù)節(jié)點(diǎn)的目錄結(jié)構(gòu)
數(shù)據(jù)節(jié)點(diǎn)的VERSION文件格式如下:
namespaceID=1232737062
storageID=DS-1640411682-127.0.1.1-50010-1254997319480
cTime=0
storageType=DATA_NODE
layoutVersion=-18
blk_<id>保存的是HDFS的數(shù)據(jù)塊,其中保存了具體的二進(jìn)制數(shù)據(jù)。
blk_<id>.meta保存的是數(shù)據(jù)塊的屬性信息:版本信息,類型信息,和checksum
當(dāng)一個(gè)目錄中的數(shù)據(jù)塊到達(dá)一定數(shù)量的時(shí)候,則創(chuàng)建子文件夾來(lái)保存數(shù)據(jù)塊及數(shù)據(jù)塊屬性信息。
2.5 文件系統(tǒng)命名空間映像文件及修改日志
當(dāng)文件系統(tǒng)客戶端(client)進(jìn)行寫操作時(shí),首先把它記錄在修改日志中(edit log)
元數(shù)據(jù)節(jié)點(diǎn)在內(nèi)存中保存了文件系統(tǒng)的元數(shù)據(jù)信息。在記錄了修改日志后,元數(shù)據(jù)節(jié)點(diǎn)則修改內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)。
每次的寫操作成功之前,修改日志都會(huì)同步(sync)到文件系統(tǒng)。
fsimage文件,也即命名空間映像文件,是內(nèi)存中的元數(shù)據(jù)在硬盤上的checkpoint,它是一種序列化的格式,并不能夠在硬盤上直接修改。
同數(shù)據(jù)的機(jī)制相似,當(dāng)元數(shù)據(jù)節(jié)點(diǎn)失敗時(shí),則最新checkpoint的元數(shù)據(jù)信息從fsimage加載到內(nèi)存中,然后逐一重新執(zhí)行修改日志中的操作。
從元數(shù)據(jù)節(jié)點(diǎn)就是用來(lái)幫助元數(shù)據(jù)節(jié)點(diǎn)將內(nèi)存中的元數(shù)據(jù)信息checkpoint到硬盤上的
checkpoint的過(guò)程如下:
從元數(shù)據(jù)節(jié)點(diǎn)通知元數(shù)據(jù)節(jié)點(diǎn)生成新的日志文件,以后的日志都寫到新的日志文件中。
從元數(shù)據(jù)節(jié)點(diǎn)用http get從元數(shù)據(jù)節(jié)點(diǎn)獲得fsimage文件及舊的日志文件。
從元數(shù)據(jù)節(jié)點(diǎn)將fsimage文件加載到內(nèi)存中,并執(zhí)行日志文件中的操作,然后生成新的fsimage文件。
從元數(shù)據(jù)節(jié)點(diǎn)獎(jiǎng)新的fsimage文件用http post傳回元數(shù)據(jù)節(jié)點(diǎn)
元數(shù)據(jù)節(jié)點(diǎn)可以將舊的fsimage文件及舊的日志文件,換為新的fsimage文件和新的日志文件(第一步生成的),然后更新fstime文件,寫入此次checkpoint的時(shí)間。
這樣元數(shù)據(jù)節(jié)點(diǎn)中的fsimage文件保存了最新的checkpoint的元數(shù)據(jù)信息,日志文件也重新開始,不會(huì)變的很大了。
#p#
3、HDFS體系結(jié)構(gòu)
HDFS是一個(gè)主/從(Mater/Slave)體系結(jié)構(gòu),從最終用戶的角度來(lái)看,它就像傳統(tǒng)的文件系統(tǒng)一樣,可以通過(guò)目錄路徑對(duì)文件執(zhí)行CRUD(Create、Read、Update和Delete)操作。但由于分布式存儲(chǔ)的性質(zhì),HDFS集群擁有一個(gè)NameNode和一些DataNode。NameNode管理文件系統(tǒng)的元數(shù)據(jù),DataNode存儲(chǔ)實(shí)際的數(shù)據(jù)??蛻舳送ㄟ^(guò)同NameNode和DataNodes的交互訪問(wèn)文件系統(tǒng)??蛻舳寺?lián)系NameNode以獲取文件的元數(shù)據(jù),而真正的文件I/O操作是直接和DataNode進(jìn)行交互的。
圖3.1 HDFS總體結(jié)構(gòu)示意圖
1)NameNode、DataNode和Client
NameNode可以看作是分布式文件系統(tǒng)中的管理者,主要負(fù)責(zé)管理文件系統(tǒng)的命名空間、集群配置信息和存儲(chǔ)塊的復(fù)制等。NameNode會(huì)將文件系統(tǒng)的Meta-data存儲(chǔ)在內(nèi)存中,這些信息主要包括了文件信息、每一個(gè)文件對(duì)應(yīng)的文件塊的信息和每一個(gè)文件塊在DataNode的信息等。
DataNode是文件存儲(chǔ)的基本單元,它將Block存儲(chǔ)在本地文件系統(tǒng)中,保存了Block的Meta-data,同時(shí)周期性地將所有存在的Block信息發(fā)送給NameNode。
Client就是需要獲取分布式文件系統(tǒng)文件的應(yīng)用程序。
2)文件寫入
Client向NameNode發(fā)起文件寫入的請(qǐng)求。
NameNode根據(jù)文件大小和文件塊配置情況,返回給Client它所管理部分DataNode的信息。
Client將文件劃分為多個(gè)Block,根據(jù)DataNode的地址信息,按順序?qū)懭氲矫恳粋€(gè)DataNode塊中。
3)文件讀取
Client向NameNode發(fā)起文件讀取的請(qǐng)求。
NameNode返回文件存儲(chǔ)的DataNode的信息。
Client讀取文件信息。
HDFS典型的部署是在一個(gè)專門的機(jī)器上運(yùn)行NameNode,集群中的其他機(jī)器各運(yùn)行一個(gè)DataNode;也可以在運(yùn)行NameNode的機(jī)器上同時(shí)運(yùn)行DataNode,或者一臺(tái)機(jī)器上運(yùn)行多個(gè)DataNode。一個(gè)集群只有一個(gè)NameNode的設(shè)計(jì)大大簡(jiǎn)化了系統(tǒng)架構(gòu)。
4、HDFS的優(yōu)缺點(diǎn)
4.1 HDFS的優(yōu)點(diǎn)
1)處理超大文件
這里的超大文件通常是指百M(fèi)B、設(shè)置數(shù)百TB大小的文件。目前在實(shí)際應(yīng)用中,HDFS已經(jīng)能用來(lái)存儲(chǔ)管理PB級(jí)的數(shù)據(jù)了。
2)流式的訪問(wèn)數(shù)據(jù)
HDFS的設(shè)計(jì)建立在更多地響應(yīng)"一次寫入、多次讀寫"任務(wù)的基礎(chǔ)上。這意味著一個(gè)數(shù)據(jù)集一旦由數(shù)據(jù)源生成,就會(huì)被復(fù)制分發(fā)到不同的存儲(chǔ)節(jié)點(diǎn)中,然后響應(yīng)各種各樣的數(shù)據(jù)分析任務(wù)請(qǐng)求。在多數(shù)情況下,分析任務(wù)都會(huì)涉及數(shù)據(jù)集中的大部分?jǐn)?shù)據(jù),也就是說(shuō),對(duì)HDFS來(lái)說(shuō),請(qǐng)求讀取整個(gè)數(shù)據(jù)集要比讀取一條記錄更加高效。
3)運(yùn)行于廉價(jià)的商用機(jī)器集群上
Hadoop設(shè)計(jì)對(duì)硬件需求比較低,只須運(yùn)行在低廉的商用硬件集群上,而無(wú)需昂貴的高可用性機(jī)器上。廉價(jià)的商用機(jī)也就意味著大型集群中出現(xiàn)節(jié)點(diǎn)故障情況的概率非常高。這就要求設(shè)計(jì)HDFS時(shí)要充分考慮數(shù)據(jù)的可靠性,安全性及高可用性。
4.2 HDFS的缺點(diǎn)
1)不適合低延遲數(shù)據(jù)訪問(wèn)
如果要處理一些用戶要求時(shí)間比較短的低延遲應(yīng)用請(qǐng)求,則HDFS不適合。HDFS是為了處理大型數(shù)據(jù)集分析任務(wù)的,主要是為達(dá)到高的數(shù)據(jù)吞吐量而設(shè)計(jì)的,這就可能要求以高延遲作為代價(jià)。
改進(jìn)策略:對(duì)于那些有低延時(shí)要求的應(yīng)用程序,HBase是一個(gè)更好的選擇。通過(guò)上層數(shù)據(jù)管理項(xiàng)目來(lái)盡可能地彌補(bǔ)這個(gè)不足。在性能上有了很大的提升,它的口號(hào)就是goes real time。使用緩存或多master設(shè)計(jì)可以降低client的數(shù)據(jù)請(qǐng)求壓力,以減少延時(shí)。還有就是對(duì)HDFS系統(tǒng)內(nèi)部的修改,這就得權(quán)衡大吞吐量與低延時(shí)了,HDFS不是萬(wàn)能的銀彈。
2)無(wú)法高效存儲(chǔ)大量小文件
因?yàn)镹amenode把文件系統(tǒng)的元數(shù)據(jù)放置在內(nèi)存中,所以文件系統(tǒng)所能容納的文件數(shù)目是由Namenode的內(nèi)存大小來(lái)決定。一般來(lái)說(shuō),每一個(gè)文件、文件夾和Block需要占據(jù)150字節(jié)左右的空間,所以,如果你有100萬(wàn)個(gè)文件,每一個(gè)占據(jù)一個(gè)Block,你就至少需要300MB內(nèi)存。當(dāng)前來(lái)說(shuō),數(shù)百萬(wàn)的文件還是可行的,當(dāng)擴(kuò)展到數(shù)十億時(shí),對(duì)于當(dāng)前的硬件水平來(lái)說(shuō)就沒法實(shí)現(xiàn)了。還有一個(gè)問(wèn)題就是,因?yàn)镸ap task的數(shù)量是由splits來(lái)決定的,所以用MR處理大量的小文件時(shí),就會(huì)產(chǎn)生過(guò)多的Maptask,線程管理開銷將會(huì)增加作業(yè)時(shí)間。舉個(gè)例子,處理10000M的文件,若每個(gè)split為1M,那就會(huì)有10000個(gè)Maptasks,會(huì)有很大的線程開銷;若每個(gè)split為100M,則只有100個(gè)Maptasks,每個(gè)Maptask將會(huì)有更多的事情做,而線程的管理開銷也將減小很多。
改進(jìn)策略:要想讓HDFS能處理好小文件,有不少方法。
利用SequenceFile、MapFile、Har等方式歸檔小文件,這個(gè)方法的原理就是把小文件歸檔起來(lái)管理,HBase就是基于此的。對(duì)于這種方法,如果想找回原來(lái)的小文件內(nèi)容,那就必須得知道與歸檔文件的映射關(guān)系。
橫向擴(kuò)展,一個(gè)Hadoop集群能管理的小文件有限,那就把幾個(gè)Hadoop集群拖在一個(gè)虛擬服務(wù)器后面,形成一個(gè)大的Hadoop集群。google也是這么干過(guò)的。
多Master設(shè)計(jì),這個(gè)作用顯而易見了。正在研發(fā)中的GFS II也要改為分布式多Master設(shè)計(jì),還支持Master的Failover,而且Block大小改為1M,有意要調(diào)優(yōu)處理小文件啊。
附帶個(gè)Alibaba DFS的設(shè)計(jì),也是多Master設(shè)計(jì),它把Metadata的映射存儲(chǔ)和管理分開了,由多個(gè)Metadata存儲(chǔ)節(jié)點(diǎn)和一個(gè)查詢Master節(jié)點(diǎn)組成。
3)不支持多用戶寫入及任意修改文件
在HDFS的一個(gè)文件中只有一個(gè)寫入者,而且寫操作只能在文件末尾完成,即只能執(zhí)行追加操作。目前HDFS還不支持多個(gè)用戶對(duì)同一文件的寫操作,以及在文件任意位置進(jìn)行修改。
#p#
5、HDFS常用操作
先說(shuō)一下"hadoop fs 和hadoop dfs的區(qū)別",看兩本Hadoop書上各有用到,但效果一樣,求證與網(wǎng)絡(luò)發(fā)現(xiàn)下面一解釋比較中肯。
粗略的講,fs是個(gè)比較抽象的層面,在分布式環(huán)境中,fs就是dfs,但在本地環(huán)境中,fs是local file system,這個(gè)時(shí)候dfs就不能用。
5.1 文件操作
1)列出HDFS文件
此處為你展示如何通過(guò)"-ls"命令列出HDFS下的文件:
hadoop fs -ls
執(zhí)行結(jié)果如圖5-1-1所示。在這里需要注意:在HDFS中未帶參數(shù)的"-ls"命名沒有返回任何值,它默認(rèn)返回HDFS的"home"目錄下的內(nèi)容。在HDFS中,沒有當(dāng)前目錄這樣一個(gè)概念,也沒有cd這個(gè)命令。
圖5-1-1 列出HDFS文件
2)列出HDFS目錄下某個(gè)文檔中的文件
此處為你展示如何通過(guò)"-ls 文件名"命令瀏覽HDFS下名為"input"的文檔中文件:
hadoop fs –ls input
執(zhí)行結(jié)果如圖5-1-2所示。
圖5-1-2 列出HDFS下名為input的文檔下的文件
3)上傳文件到HDFS
此處為你展示如何通過(guò)"-put 文件1 文件2"命令將"Master.Hadoop"機(jī)器下的"/home/hadoop"目錄下的file文件上傳到HDFS上并重命名為test:
hadoop fs –put ~/file test
執(zhí)行結(jié)果如圖5-1-3所示。在執(zhí)行"-put"時(shí)只有兩種可能,即是執(zhí)行成功和執(zhí)行失敗。在上傳文件時(shí),文件首先復(fù)制到DataNode上。只有所有的DataNode都成功接收完數(shù)據(jù),文件上傳才是成功的。其他情況(如文件上傳終端等)對(duì)HDFS來(lái)說(shuō)都是做了無(wú)用功。
圖5-1-3 成功上傳file到HDFS
4)將HDFS中文件復(fù)制到本地系統(tǒng)中
此處為你展示如何通過(guò)"-get 文件1 文件2"命令將HDFS中的"output"文件復(fù)制到本地系統(tǒng)并命名為"getout"。
hadoop fs –get output getout
執(zhí)行結(jié)果如圖5-1-4所示。
圖5-1-4 成功將HDFS中output文件復(fù)制到本地系統(tǒng)
備注:與"-put"命令一樣,"-get"操作既可以操作文件,也可以操作目錄。
5)刪除HDFS下的文檔
此處為你展示如何通過(guò)"-rmr 文件"命令刪除HDFS下名為"newoutput"的文檔:
hadoop fs –rmr newoutput
執(zhí)行結(jié)果如圖5-1-5所示。
圖5-1-5 成功刪除HDFS下的newoutput文檔
6)查看HDFS下某個(gè)文件
此處為你展示如何通過(guò)"-cat 文件"命令查看HDFS下input文件中內(nèi)容:
hadoop fs -cat input/*
執(zhí)行結(jié)果如圖5-1-6所示。
圖5-1-6 HDFS下input文件的內(nèi)容
"hadoop fs"的命令遠(yuǎn)不止這些,本小節(jié)介紹的命令已可以在HDFS上完成大多數(shù)常規(guī)操作。對(duì)于其他操作,可以通過(guò)"-help commandName"命令所列出的清單來(lái)進(jìn)一步學(xué)習(xí)與探索。
5.2 管理與更新
1)報(bào)告HDFS的基本統(tǒng)計(jì)情況
此處為你展示通過(guò)"-report"命令如何查看HDFS的基本統(tǒng)計(jì)信息:
hadoop dfsadmin -report
執(zhí)行結(jié)果如圖5-2-1所示。
圖5-2-1 HDFS基本統(tǒng)計(jì)信息
2)退出安全模式
NameNode在啟動(dòng)時(shí)會(huì)自動(dòng)進(jìn)入安全模式。安全模式是NameNode的一種狀態(tài),在這個(gè)階段,文件系統(tǒng)不允許有任何修改。安全模式的目的是在系統(tǒng)啟動(dòng)時(shí)檢查各個(gè)DataNode上數(shù)據(jù)塊的有效性,同時(shí)根據(jù)策略對(duì)數(shù)據(jù)塊進(jìn)行必要的復(fù)制或刪除,當(dāng)數(shù)據(jù)塊最小百分比數(shù)滿足的最小副本數(shù)條件時(shí),會(huì)自動(dòng)退出安全模式。
系統(tǒng)顯示"Name node is in safe mode",說(shuō)明系統(tǒng)正處于安全模式,這時(shí)只需要等待17秒即可,也可以通過(guò)下面的命令退出安全模式:
hadoop dfsadmin –safemode enter
成功退出安全模式結(jié)果如圖5-2-2所示。
圖5-2-2 成功退出安全模式
3)進(jìn)入安全模式
在必要情況下,可以通過(guò)以下命令把HDFS置于安全模式:
hadoop dfsadmin –safemode enter
執(zhí)行結(jié)果如圖5-2-3所示。
圖5-2-3 進(jìn)入HDFS安全模式
4)添加節(jié)點(diǎn)
可擴(kuò)展性是HDFS的一個(gè)重要特性,向HDFS集群中添加節(jié)點(diǎn)是很容易實(shí)現(xiàn)的。添加一個(gè)新的DataNode節(jié)點(diǎn),首先在新加節(jié)點(diǎn)上安裝好Hadoop,要和NameNode使用相同的配置(可以直接從NameNode復(fù)制),修改"/usr/hadoop/conf/master"文件,加入NameNode主機(jī)名。然后在NameNode節(jié)點(diǎn)上修改"/usr/hadoop/conf/slaves"文件,加入新節(jié)點(diǎn)主機(jī)名,再建立到新加點(diǎn)無(wú)密碼的SSH連接,運(yùn)行啟動(dòng)命令:
start-all.sh
5)負(fù)載均衡
HDFS的數(shù)據(jù)在各個(gè)DataNode中的分布肯能很不均勻,尤其是在DataNode節(jié)點(diǎn)出現(xiàn)故障或新增DataNode節(jié)點(diǎn)時(shí)。新增數(shù)據(jù)塊時(shí)NameNode對(duì)DataNode節(jié)點(diǎn)的選擇策略也有可能導(dǎo)致數(shù)據(jù)塊分布的不均勻。用戶可以使用命令重新平衡DataNode上的數(shù)據(jù)塊的分布:
start-balancer.sh
執(zhí)行命令前,DataNode節(jié)點(diǎn)上數(shù)據(jù)分布情況如圖5-2-4所示。
負(fù)載均衡完畢后,DataNode節(jié)點(diǎn)上數(shù)據(jù)的分布情況如圖5-2-5所示。
執(zhí)行負(fù)載均衡命令如圖5-2-6所示。
6、HDFS API詳解
Hadoop中關(guān)于文件操作類基本上全部是在"org.apache.hadoop.fs"包中,這些API能夠支持的操作包含:打開文件,讀寫文件,刪除文件等。
Hadoop類庫(kù)中最終面向用戶提供的接口類是FileSystem,該類是個(gè)抽象類,只能通過(guò)來(lái)類的get方法得到具體類。get方法存在幾個(gè)重載版本,常用的是這個(gè):
static FileSystem get(Configuration conf);
該類封裝了幾乎所有的文件操作,例如mkdir,delete等。綜上基本上可以得出操作文件的程序庫(kù)框架:
- operator()
- {
- 得到Configuration對(duì)象
- 得到FileSystem對(duì)象
- 進(jìn)行文件操作
- }
6.1 上傳本地文件
通過(guò)"FileSystem.copyFromLocalFile(Path src,Patch dst)"可將本地文件上傳到HDFS的制定位置上,其中src和dst均為文件的完整路徑。具體事例如下
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileStatus;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class CopyFile {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- //本地文件
- Path src =new Path("D:\\HebutWinOS");
- //HDFS為止
- Path dst =new Path("/");
- hdfs.copyFromLocalFile(src, dst);
- System.out.println("Upload to"+conf.get("fs.default.name"));
- FileStatus files[]=hdfs.listStatus(dst);
- for(FileStatus file:files){
- System.out.println(file.getPath());
- }
- }
- }
運(yùn)行結(jié)果可以通過(guò)控制臺(tái)、項(xiàng)目瀏覽器和SecureCRT查看,如圖6-1-1、圖6-1-2、圖6-1-3所示。
1)控制臺(tái)結(jié)果
圖6-1-1 運(yùn)行結(jié)果(1)
2)項(xiàng)目瀏覽器
圖6-1-2 運(yùn)行結(jié)果(2)
3)SecureCRT結(jié)果
圖6-1-3 運(yùn)行結(jié)果(3)
6.2 創(chuàng)建HDFS文件
通過(guò)"FileSystem.create(Path f)"可在HDFS上創(chuàng)建文件,其中f為文件的完整路徑。具體實(shí)現(xiàn)如下:
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FSDataOutputStream;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class CreateFile {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- byte[] buff="hello hadoop world!\n".getBytes();
- Path dfs=new Path("/test");
- FSDataOutputStream outputStream=hdfs.create(dfs);
- outputStream.write(buff,0,buff.length);
- }
- }
運(yùn)行結(jié)果如圖6-2-1和圖6-2-2所示。
1)項(xiàng)目瀏覽器
圖6-2-1 運(yùn)行結(jié)果(1)
2)SecureCRT結(jié)果
圖6-2-2 運(yùn)行結(jié)果(2)
6.3 創(chuàng)建HDFS目錄
通過(guò)"FileSystem.mkdirs(Path f)"可在HDFS上創(chuàng)建文件夾,其中f為文件夾的完整路徑。具體實(shí)現(xiàn)如下:
- package com.hebut.dir;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class CreateDir {
- public static void main(String[] args) throws Exception{
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- Path dfs=new Path("/TestDir");
- hdfs.mkdirs(dfs);
- }
- }
運(yùn)行結(jié)果如圖6-3-1和圖6-3-2所示。
1)項(xiàng)目瀏覽器
圖6-3-1 運(yùn)行結(jié)果(1)
2)SecureCRT結(jié)果
圖6-3-2 運(yùn)行結(jié)果(2)
6.4 重命名HDFS文件
通過(guò)"FileSystem.rename(Path src,Path dst)"可為指定的HDFS文件重命名,其中src和dst均為文件的完整路徑。具體實(shí)現(xiàn)如下:
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class Rename{
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- Path frpaht=new Path("/test"); //舊的文件名
- Path topath=new Path("/test1"); //新的文件名
- boolean isRename=hdfs.rename(frpaht, topath);
- String result=isRename?"成功":"失敗";
- System.out.println("文件重命名結(jié)果為:"+result);
- }
- }
運(yùn)行結(jié)果如圖6-4-1和圖6-4-2所示。
1)項(xiàng)目瀏覽器
圖6-4-1 運(yùn)行結(jié)果(1)
2)SecureCRT結(jié)果
圖6-4-2 運(yùn)行結(jié)果(2)
6.5 刪除HDFS上的文件
通過(guò)"FileSystem.delete(Path f,Boolean recursive)"可刪除指定的HDFS文件,其中f為需要?jiǎng)h除文件的完整路徑,recuresive用來(lái)確定是否進(jìn)行遞歸刪除。具體實(shí)現(xiàn)如下
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class DeleteFile {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- Path delef=new Path("/test1");
- boolean isDeleted=hdfs.delete(delef,false);
- //遞歸刪除
- //boolean isDeleted=hdfs.delete(delef,true);
- System.out.println("Delete?"+isDeleted);
- }
- }
運(yùn)行結(jié)果如圖6-5-1和圖6-5-2所示。
1)控制臺(tái)結(jié)果
圖6-5-1 運(yùn)行結(jié)果(1)
2)項(xiàng)目瀏覽器
圖6-5-2 運(yùn)行結(jié)果(2)
6.6 刪除HDFS上的目錄
同刪除文件代碼一樣,只是換成刪除目錄路徑即可,如果目錄下有文件,要進(jìn)行遞歸刪除。
6.7 查看某個(gè)HDFS文件是否存在
通過(guò)"FileSystem.exists(Path f)"可查看指定HDFS文件是否存在,其中f為文件的完整路徑。具體實(shí)現(xiàn)如下:
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class CheckFile {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- Path findf=new Path("/test1");
- boolean isExists=hdfs.exists(findf);
- System.out.println("Exist?"+isExists);
- }
- }
運(yùn)行結(jié)果如圖6-7-1和圖6-7-2所示。
1)控制臺(tái)結(jié)果
圖6-7-1 運(yùn)行結(jié)果(1)
2)項(xiàng)目瀏覽器
圖6-7-2 運(yùn)行結(jié)果(2)
6.8 查看HDFS文件的最后修改時(shí)間
通過(guò)"FileSystem.getModificationTime()"可查看指定HDFS文件的修改時(shí)間。具體實(shí)現(xiàn)如下:
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileStatus;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class GetLTime {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- Path fpath =new Path("/user/hadoop/test/file1.txt");
- FileStatus fileStatus=hdfs.getFileStatus(fpath);
- long modiTime=fileStatus.getModificationTime();
- System.out.println("file1.txt的修改時(shí)間是"+modiTime);
- }
- }
運(yùn)行結(jié)果如圖6-8-1所示。
圖6-8-1 控制臺(tái)結(jié)果
6.9 讀取HDFS某個(gè)目錄下的所有文件
通過(guò)"FileStatus.getPath()"可查看指定HDFS中某個(gè)目錄下所有文件。具體實(shí)現(xiàn)如下:
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileStatus;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class ListAllFile {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- Path listf =new Path("/user/hadoop/test");
- FileStatus stats[]=hdfs.listStatus(listf);
- for(int i = 0; i < stats.length; ++i)
- {
- System.out.println(stats[i].getPath().toString());
- }
- hdfs.close();
- }
- }
運(yùn)行結(jié)果如圖6-9-1和圖6-9-2所示。
1)控制臺(tái)結(jié)果
圖6-9-1 運(yùn)行結(jié)果(1)
2)項(xiàng)目瀏覽器
圖6-9-2 運(yùn)行結(jié)果(2)
6.10 查找某個(gè)文件在HDFS集群的位置
通過(guò)"FileSystem.getFileBlockLocation(FileStatus file,long start,long len)"可查找指定文件在HDFS集群上的位置,其中file為文件的完整路徑,start和len來(lái)標(biāo)識(shí)查找文件的路徑。具體實(shí)現(xiàn)如下:
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.BlockLocation;
- import org.apache.hadoop.fs.FileStatus;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- public class FileLoc {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem hdfs=FileSystem.get(conf);
- Path fpath=new Path("/user/hadoop/cygwin");
- FileStatus filestatus = hdfs.getFileStatus(fpath);
- BlockLocation[] blkLocations = hdfs.getFileBlockLocations(filestatus, 0, filestatus.getLen());
- int blockLen = blkLocations.length;
- for(int i=0;i<blockLen;i++){
- String[] hosts = blkLocations[i].getHosts();
- System.out.println("block_"+i+"_location:"+hosts[0]);
- }
- }
- }
運(yùn)行結(jié)果如圖6-10-1和6.10.2所示。
1)控制臺(tái)結(jié)果
圖6-10-1 運(yùn)行結(jié)果(1)
2)項(xiàng)目瀏覽器
圖6-10-2 運(yùn)行結(jié)果(2)
6.11 獲取HDFS集群上所有節(jié)點(diǎn)名稱信息
通過(guò)"DatanodeInfo.getHostName()"可獲取HDFS集群上的所有節(jié)點(diǎn)名稱。具體實(shí)現(xiàn)如下:
- package com.hebut.file;
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.hdfs.DistributedFileSystem;
- import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
- public class GetList {
- public static void main(String[] args) throws Exception {
- Configuration conf=new Configuration();
- FileSystem fs=FileSystem.get(conf);
- DistributedFileSystem hdfs = (DistributedFileSystem)fs;
- DatanodeInfo[] dataNodeStats = hdfs.getDataNodeStats();
- for(int i=0;i<dataNodeStats.length;i++){
- System.out.println("DataNode_"+i+"_Name:"+dataNodeStats[i].getHostName());
- }
- }
- }
運(yùn)行結(jié)果如圖6-11-1所示。
圖6-11-1 控制臺(tái)結(jié)果
7、HDFS的讀寫數(shù)據(jù)流
7.1 文件的讀取剖析
文件讀取的過(guò)程如下:
1)解釋一
-
客戶端(client)用FileSystem的open()函數(shù)打開文件。
-
DistributedFileSystem用RPC調(diào)用元數(shù)據(jù)節(jié)點(diǎn),得到文件的數(shù)據(jù)塊信息。
-
對(duì)于每一個(gè)數(shù)據(jù)塊,元數(shù)據(jù)節(jié)點(diǎn)返回保存數(shù)據(jù)塊的數(shù)據(jù)節(jié)點(diǎn)的地址。
-
DistributedFileSystem返回FSDataInputStream給客戶端,用來(lái)讀取數(shù)據(jù)。
-
客戶端調(diào)用stream的read()函數(shù)開始讀取數(shù)據(jù)。
-
DFSInputStream連接保存此文件第一個(gè)數(shù)據(jù)塊的最近的數(shù)據(jù)節(jié)點(diǎn)。
-
Data從數(shù)據(jù)節(jié)點(diǎn)讀到客戶端(client)。
-
當(dāng)此數(shù)據(jù)塊讀取完畢時(shí),DFSInputStream關(guān)閉和此數(shù)據(jù)節(jié)點(diǎn)的連接,然后連接此文件下一個(gè)數(shù)據(jù)塊的最近的數(shù)據(jù)節(jié)點(diǎn)。
-
當(dāng)客戶端讀取完畢數(shù)據(jù)的時(shí)候,調(diào)用FSDataInputStream的close函數(shù)。
-
在讀取數(shù)據(jù)的過(guò)程中,如果客戶端在與數(shù)據(jù)節(jié)點(diǎn)通信出現(xiàn)錯(cuò)誤,則嘗試連接包含此數(shù)據(jù)塊的下一個(gè)數(shù)據(jù)節(jié)點(diǎn)。
-
失敗的數(shù)據(jù)節(jié)點(diǎn)將被記錄,以后不再連接。
2)解釋二
-
使用HDFS提供的客戶端開發(fā)庫(kù),向遠(yuǎn)程的Namenode發(fā)起RPC請(qǐng)求;
-
Namenode會(huì)視情況返回文件的部分或者全部block列表,對(duì)于每個(gè)block,Namenode都會(huì)返回有該block拷貝的datanode地址;
-
客戶端開發(fā)庫(kù)會(huì)選取離客戶端最接近的datanode來(lái)讀取block;
-
讀取完當(dāng)前block的數(shù)據(jù)后,關(guān)閉與當(dāng)前的datanode連接,并為讀取下一個(gè)block尋找最佳的datanode;
-
當(dāng)讀完列表的block后,且文件讀取還沒有結(jié)束,客戶端開發(fā)庫(kù)會(huì)繼續(xù)向Namenode獲取下一批的block列表。
-
讀取完一個(gè)block都會(huì)進(jìn)行checksum驗(yàn)證,如果讀取datanode時(shí)出現(xiàn)錯(cuò)誤,客戶端會(huì)通知Namenode,然后再?gòu)南乱粋€(gè)擁有該block拷貝的datanode繼續(xù)讀。
7.2 文件的寫入剖析
寫入文件的過(guò)程比讀取較為復(fù)雜:
1)解釋一
-
客戶端調(diào)用create()來(lái)創(chuàng)建文件
-
DistributedFileSystem用RPC調(diào)用元數(shù)據(jù)節(jié)點(diǎn),在文件系統(tǒng)的命名空間中創(chuàng)建一個(gè)新的文件。
-
元數(shù)據(jù)節(jié)點(diǎn)首先確定文件原來(lái)不存在,并且客戶端有創(chuàng)建文件的權(quán)限,然后創(chuàng)建新文件。
-
DistributedFileSystem返回DFSOutputStream,客戶端用于寫數(shù)據(jù)。
-
客戶端開始寫入數(shù)據(jù),DFSOutputStream將數(shù)據(jù)分成塊,寫入data queue。
-
Data queue由Data Streamer讀取,并通知元數(shù)據(jù)節(jié)點(diǎn)分配數(shù)據(jù)節(jié)點(diǎn),用來(lái)存儲(chǔ)數(shù)據(jù)塊(每塊默認(rèn)復(fù)制3塊)。分配的數(shù)據(jù)節(jié)點(diǎn)放在一個(gè)pipeline里。
-
Data Streamer將數(shù)據(jù)塊寫入pipeline中的第一個(gè)數(shù)據(jù)節(jié)點(diǎn)。第一個(gè)數(shù)據(jù)節(jié)點(diǎn)將數(shù)據(jù)塊發(fā)送給第二個(gè)數(shù)據(jù)節(jié)點(diǎn)。第二個(gè)數(shù)據(jù)節(jié)點(diǎn)將數(shù)據(jù)發(fā)送給第三個(gè)數(shù)據(jù)節(jié)點(diǎn)。
-
DFSOutputStream為發(fā)出去的數(shù)據(jù)塊保存了ack queue,等待pipeline中的數(shù)據(jù)節(jié)點(diǎn)告知數(shù)據(jù)已經(jīng)寫入成功。
-
如果數(shù)據(jù)節(jié)點(diǎn)在寫入的過(guò)程中失?。?/p>
-
關(guān)閉pipeline,將ack queue中的數(shù)據(jù)塊放入data queue的開始。
-
當(dāng)前的數(shù)據(jù)塊在已經(jīng)寫入的數(shù)據(jù)節(jié)點(diǎn)中被元數(shù)據(jù)節(jié)點(diǎn)賦予新的標(biāo)示,則錯(cuò)誤節(jié)點(diǎn)重啟后能夠察覺其數(shù)據(jù)塊是過(guò)時(shí)的,會(huì)被刪除。
-
失敗的數(shù)據(jù)節(jié)點(diǎn)從pipeline中移除,另外的數(shù)據(jù)塊則寫入pipeline中的另外兩個(gè)數(shù)據(jù)節(jié)點(diǎn)。
-
元數(shù)據(jù)節(jié)點(diǎn)則被通知此數(shù)據(jù)塊是復(fù)制塊數(shù)不足,將來(lái)會(huì)再創(chuàng)建第三份備份。
-
-
當(dāng)客戶端結(jié)束寫入數(shù)據(jù),則調(diào)用stream的close函數(shù)。此操作將所有的數(shù)據(jù)塊寫入pipeline中的數(shù)據(jù)節(jié)點(diǎn),并等待ack queue返回成功。最后通知元數(shù)據(jù)節(jié)點(diǎn)寫入完畢。
2)解釋二
-
使用HDFS提供的客戶端開發(fā)庫(kù),向遠(yuǎn)程的Namenode發(fā)起RPC請(qǐng)求;
-
Namenode會(huì)檢查要?jiǎng)?chuàng)建的文件是否已經(jīng)存在,創(chuàng)建者是否有權(quán)限進(jìn)行操作,成功則會(huì)為文件創(chuàng)建一個(gè)記錄,否則會(huì)讓客戶端拋出異常;
-
當(dāng)客戶端開始寫入文件的時(shí)候,開發(fā)庫(kù)會(huì)將文件切分成多個(gè)packets,并在內(nèi)部以"data queue"的形式管理這些packets,并向Namenode申請(qǐng)新的blocks,獲取用來(lái)存儲(chǔ)replicas的合適的datanodes列表,列表的大小根據(jù)在Namenode中對(duì)replication的設(shè)置而定。
-
開始以pipeline(管道)的形式將packet寫入所有的replicas中。開發(fā)庫(kù)把packet以流的方式寫入第一個(gè)datanode,該datanode把該packet存儲(chǔ)之后,再將其傳遞給在此pipeline中的下一個(gè)datanode,直到最后一個(gè)datanode,這種寫數(shù)據(jù)的方式呈流水線的形式。
-
最后一個(gè)datanode成功存儲(chǔ)之后會(huì)返回一個(gè)ack packet,在pipeline里傳遞至客戶端,在客戶端的開發(fā)庫(kù)內(nèi)部維護(hù)著"ack queue",成功收到datanode返回的ack packet后會(huì)從"ack queue"移除相應(yīng)的packet。
-
如果傳輸過(guò)程中,有某個(gè)datanode出現(xiàn)了故障,那么當(dāng)前的pipeline會(huì)被關(guān)閉,出現(xiàn)故障的datanode會(huì)從當(dāng)前的pipeline中移除,剩余的block會(huì)繼續(xù)剩下的datanode中繼續(xù)以pipeline的形式傳輸,同時(shí)Namenode會(huì)分配一個(gè)新的datanode,保持replicas設(shè)定的數(shù)量。