HDFS体系结构中有两类节点,一类是NameNode,又叫"元数据节点";另一类是DataNode,又叫"数据节点"。这两类节点分别承担Master和Worker具体任务的执行节点。
1)元数据节点用来管理文件系统的命名空间
2)数据节点是文件系统中真正存储数据的地方。
3)从元数据节点(secondarynamenode)
namespaceID=1232737062
cTime=0
storageType=NAME_NODE
layoutVersion=-18
storageID=DS-1640411682-127.0.1.1-50010-1254997319480
storageType=DATA_NODE
HDFS是一个主/从(Mater/Slave)体系结构,从最终用户的角度来看,它就像传统的文件系统一样,可以通过目录路径对文件执行CRUD(Create、Read、Update和Delete)操作。但由于分布式存储的性质,HDFS集群拥有一个NameNode和一些DataNode。NameNode管理文件系统的元数据,DataNode存储实际的数据。客户端通过同NameNode和DataNodes的交互访问文件系统。客户端联系NameNode以获取文件的元数据,而真正的文件I/O操作是直接和DataNode进行交互的。
图3.1HDFS总体结构示意图
1)NameNode、DataNode和Client
2)文件写入
3)文件读取
HDFS典型的部署是在一个专门的机器上运行NameNode,集群中的其他机器各运行一个DataNode;也可以在运行NameNode的机器上同时运行DataNode,或者一台机器上运行多个DataNode。一个集群只有一个NameNode的设计大大简化了系统架构。
1)处理超大文件
这里的超大文件通常是指百MB、设置数百TB大小的文件。目前在实际应用中,HDFS已经能用来存储管理PB级的数据了。
2)流式的访问数据
HDFS的设计建立在更多地响应"一次写入、多次读写"任务的基础上。这意味着一个数据集一旦由数据源生成,就会被复制分发到不同的存储节点中,然后响应各种各样的数据分析任务请求。在多数情况下,分析任务都会涉及数据集中的大部分数据,也就是说,对HDFS来说,请求读取整个数据集要比读取一条记录更加高效。
3)运行于廉价的商用机器集群上
Hadoop设计对硬件需求比较低,只须运行在低廉的商用硬件集群上,而无需昂贵的高可用性机器上。廉价的商用机也就意味着大型集群中出现节点故障情况的概率非常高。这就要求设计HDFS时要充分考虑数据的可靠性,安全性及高可用性。
1)不适合低延迟数据访问
改进策略:对于那些有低延时要求的应用程序,HBase是一个更好的选择。通过上层数据管理项目来尽可能地弥补这个不足。在性能上有了很大的提升,它的口号就是goesrealtime。使用缓存或多master设计可以降低client的数据请求压力,以减少延时。还有就是对HDFS系统内部的修改,这就得权衡大吞吐量与低延时了,HDFS不是万能的银弹。
2)无法高效存储大量小文件
改进策略:要想让HDFS能处理好小文件,有不少方法。
3)不支持多用户写入及任意修改文件
在HDFS的一个文件中只有一个写入者,而且写操作只能在文件末尾完成,即只能执行追加操作。目前HDFS还不支持多个用户对同一文件的写操作,以及在文件任意位置进行修改。
先说一下"hadoopfs和hadoopdfs的区别",看两本Hadoop书上各有用到,但效果一样,求证与网络发现下面一解释比较中肯。
粗略的讲,fs是个比较抽象的层面,在分布式环境中,fs就是dfs,但在本地环境中,fs是localfilesystem,这个时候dfs就不能用。
1)列出HDFS文件
此处为你展示如何通过"-ls"命令列出HDFS下的文件:
hadoopfs-ls
执行结果如图5-1-1所示。在这里需要注意:在HDFS中未带参数的"-ls"命名没有返回任何值,它默认返回HDFS的"home"目录下的内容。在HDFS中,没有当前目录这样一个概念,也没有cd这个命令。
图5-1-1列出HDFS文件
2)列出HDFS目录下某个文档中的文件
此处为你展示如何通过"-ls文件名"命令浏览HDFS下名为"input"的文档中文件:
hadoopfs–lsinput
执行结果如图5-1-2所示。
图5-1-2列出HDFS下名为input的文档下的文件
3)上传文件到HDFS
此处为你展示如何通过"-put文件1文件2"命令将"Master.Hadoop"机器下的"/home/hadoop"目录下的file文件上传到HDFS上并重命名为test:
hadoopfs–put~/filetest
执行结果如图5-1-3所示。在执行"-put"时只有两种可能,即是执行成功和执行失败。在上传文件时,文件首先复制到DataNode上。只有所有的DataNode都成功接收完数据,文件上传才是成功的。其他情况(如文件上传终端等)对HDFS来说都是做了无用功。
图5-1-3成功上传file到HDFS
4)将HDFS中文件复制到本地系统中
此处为你展示如何通过"-get文件1文件2"命令将HDFS中的"output"文件复制到本地系统并命名为"getout"。
hadoopfs–getoutputgetout
执行结果如图5-1-4所示。
图5-1-4成功将HDFS中output文件复制到本地系统
备注:与"-put"命令一样,"-get"操作既可以操作文件,也可以操作目录。
5)删除HDFS下的文档
此处为你展示如何通过"-rmr文件"命令删除HDFS下名为"newoutput"的文档:
hadoopfs–rmrnewoutput
执行结果如图5-1-5所示。
图5-1-5成功删除HDFS下的newoutput文档
6)查看HDFS下某个文件
此处为你展示如何通过"-cat文件"命令查看HDFS下input文件中内容:
hadoopfs-catinput/*
执行结果如图5-1-6所示。
图5-1-6HDFS下input文件的内容
"hadoopfs"的命令远不止这些,本小节介绍的命令已可以在HDFS上完成大多数常规操作。对于其他操作,可以通过"-helpcommandName"命令所列出的清单来进一步学习与探索。
1)报告HDFS的基本统计情况
此处为你展示通过"-report"命令如何查看HDFS的基本统计信息:
hadoopdfsadmin-report
执行结果如图5-2-1所示。
图5-2-1HDFS基本统计信息
2)退出安全模式
NameNode在启动时会自动进入安全模式。安全模式是NameNode的一种状态,在这个阶段,文件系统不允许有任何修改。安全模式的目的是在系统启动时检查各个DataNode上数据块的有效性,同时根据策略对数据块进行必要的复制或删除,当数据块最小百分比数满足的最小副本数条件时,会自动退出安全模式。
系统显示"Namenodeisinsafemode",说明系统正处于安全模式,这时只需要等待17秒即可,也可以通过下面的命令退出安全模式:
hadoopdfsadmin–safemodeenter
成功退出安全模式结果如图5-2-2所示。
图5-2-2成功退出安全模式
3)进入安全模式
在必要情况下,可以通过以下命令把HDFS置于安全模式:
执行结果如图5-2-3所示。
图5-2-3进入HDFS安全模式
4)添加节点
可扩展性是HDFS的一个重要特性,向HDFS集群中添加节点是很容易实现的。添加一个新的DataNode节点,首先在新加节点上安装好Hadoop,要和NameNode使用相同的配置(可以直接从NameNode复制),修改"/usr/hadoop/conf/master"文件,加入NameNode主机名。然后在NameNode节点上修改"/usr/hadoop/conf/slaves"文件,加入新节点主机名,再建立到新加点无密码的SSH连接,运行启动命令:
start-all.sh
5)负载均衡
HDFS的数据在各个DataNode中的分布肯能很不均匀,尤其是在DataNode节点出现故障或新增DataNode节点时。新增数据块时NameNode对DataNode节点的选择策略也有可能导致数据块分布的不均匀。用户可以使用命令重新平衡DataNode上的数据块的分布:
start-balancer.sh
执行命令前,DataNode节点上数据分布情况如图5-2-4所示。
负载均衡完毕后,DataNode节点上数据的分布情况如图5-2-5所示。
执行负载均衡命令如图5-2-6所示。
Hadoop中关于文件操作类基本上全部是在"org.apache.hadoop.fs"包中,这些API能够支持的操作包含:打开文件,读写文件,删除文件等。
Hadoop类库中最终面向用户提供的接口类是FileSystem,该类是个抽象类,只能通过来类的get方法得到具体类。get方法存在几个重载版本,常用的是这个:
staticFileSystemget(Configurationconf);
该类封装了几乎所有的文件操作,例如mkdir,delete等。综上基本上可以得出操作文件的程序库框架:
operator()
{
得到Configuration对象
得到FileSystem对象
进行文件操作
}
通过"FileSystem.copyFromLocalFile(Pathsrc,Patchdst)"可将本地文件上传到HDFS的制定位置上,其中src和dst均为文件的完整路径。具体事例如下:
packagecom.hebut.file;
importorg.apache.hadoop.conf.Configuration;
importorg.apache.hadoop.fs.FileStatus;
importorg.apache.hadoop.fs.FileSystem;
importorg.apache.hadoop.fs.Path;
publicclassCopyFile{
publicstaticvoidmain(String[]args)throwsException{
Configurationconf=newConfiguration();
FileSystemhdfs=FileSystem.get(conf);
//本地文件
Pathsrc=newPath("D:\\HebutWinOS");
//HDFS为止
Pathdst=newPath("/");
hdfs.copyFromLocalFile(src,dst);
System.out.println("Uploadto"+conf.get("fs.default.name"));
FileStatusfiles[]=hdfs.listStatus(dst);
for(FileStatusfile:files){
System.out.println(file.getPath());
运行结果可以通过控制台、项目浏览器和SecureCRT查看,如图6-1-1、图6-1-2、图6-1-3所示。
1)控制台结果
图6-1-1运行结果(1)
2)项目浏览器
图6-1-2运行结果(2)
3)SecureCRT结果
图6-1-3运行结果(3)
通过"FileSystem.create(Pathf)"可在HDFS上创建文件,其中f为文件的完整路径。具体实现如下:
importorg.apache.hadoop.fs.FSDataOutputStream;
publicclassCreateFile{
byte[]buff="hellohadoopworld!\n".getBytes();
Pathdfs=newPath("/test");
FSDataOutputStreamoutputStream=hdfs.create(dfs);
outputStream.write(buff,0,buff.length);
运行结果如图6-2-1和图6-2-2所示。
1)项目浏览器
图6-2-1运行结果(1)
2)SecureCRT结果
图6-2-2运行结果(2)
通过"FileSystem.mkdirs(Pathf)"可在HDFS上创建文件夹,其中f为文件夹的完整路径。具体实现如下:
packagecom.hebut.dir;
publicclassCreateDir{
Pathdfs=newPath("/TestDir");
hdfs.mkdirs(dfs);
运行结果如图6-3-1和图6-3-2所示。
图6-3-1运行结果(1)
图6-3-2运行结果(2)
通过"FileSystem.rename(Pathsrc,Pathdst)"可为指定的HDFS文件重命名,其中src和dst均为文件的完整路径。具体实现如下:
publicclassRename{
Pathfrpaht=newPath("/test");//旧的文件名
Pathtopath=newPath("/test1");//新的文件名
booleanisRename=hdfs.rename(frpaht,topath);
Stringresult=isRename"成功":"失败";
System.out.println("文件重命名结果为:"+result);
运行结果如图6-4-1和图6-4-2所示。
图6-4-1运行结果(1)
图6-4-2运行结果(2)
通过"FileSystem.delete(Pathf,Booleanrecursive)"可删除指定的HDFS文件,其中f为需要删除文件的完整路径,recuresive用来确定是否进行递归删除。具体实现如下:
publicclassDeleteFile{
Pathdelef=newPath("/test1");
booleanisDeleted=hdfs.delete(delef,false);
//递归删除
//booleanisDeleted=hdfs.delete(delef,true);
System.out.println("Delete"+isDeleted);
运行结果如图6-5-1和图6-5-2所示。
图6-5-1运行结果(1)
图6-5-2运行结果(2)
同删除文件代码一样,只是换成删除目录路径即可,如果目录下有文件,要进行递归删除。
通过"FileSystem.exists(Pathf)"可查看指定HDFS文件是否存在,其中f为文件的完整路径。具体实现如下:
publicclassCheckFile{
Pathfindf=newPath("/test1");
booleanisExists=hdfs.exists(findf);
System.out.println("Exist"+isExists);
运行结果如图6-7-1和图6-7-2所示。
图6-7-1运行结果(1)
图6-7-2运行结果(2)
publicclassGetLTime{
Pathfpath=newPath("/user/hadoop/test/file1.txt");
FileStatusfileStatus=hdfs.getFileStatus(fpath);
longmodiTime=fileStatus.getModificationTime();
运行结果如图6-8-1所示。
图6-8-1控制台结果
通过"FileStatus.getPath()"可查看指定HDFS中某个目录下所有文件。具体实现如下:
publicclassListAllFile{
Pathlistf=newPath("/user/hadoop/test");
FileStatusstats[]=hdfs.listStatus(listf);
for(inti=0;i System.out.println(stats[i].getPath().toString()); hdfs.close(); 运行结果如图6-9-1和图6-9-2所示。 图6-9-1运行结果(1) 图6-9-2运行结果(2) 通过"FileSystem.getFileBlockLocation(FileStatusfile,longstart,longlen)"可查找指定文件在HDFS集群上的位置,其中file为文件的完整路径,start和len来标识查找文件的路径。具体实现如下: importorg.apache.hadoop.fs.BlockLocation; publicclassFileLoc{ Pathfpath=newPath("/user/hadoop/cygwin"); FileStatusfilestatus=hdfs.getFileStatus(fpath); BlockLocation[]blkLocations=hdfs.getFileBlockLocations(filestatus,0,filestatus.getLen()); intblockLen=blkLocations.length; for(inti=0;i String[]hosts=blkLocations[i].getHosts(); System.out.println("block_"+i+"_location:"+hosts[0]); 运行结果如图6-10-1和6.10.2所示。 图6-10-1运行结果(1) 图6-10-2运行结果(2) 通过"DatanodeInfo.getHostName()"可获取HDFS集群上的所有节点名称。具体实现如下: importorg.apache.hadoop.hdfs.DistributedFileSystem; importorg.apache.hadoop.hdfs.protocol.DatanodeInfo; publicclassGetList{ FileSystemfs=FileSystem.get(conf); DistributedFileSystemhdfs=(DistributedFileSystem)fs; DatanodeInfo[]dataNodeStats=hdfs.getDataNodeStats(); for(inti=0;i System.out.println("DataNode_"+i+"_Name:"+dataNodeStats[i].getHostName());