HDFS
HDFS概述
Hadoop Distributed File System
一种统一管理多个节点上的文件的分布式系统。
使用场景:适合一次写入,多次读出的场景,且不支持文件修改。适合用来做数据分析,并不适合做网盘应用。
HDFS优点
1)高容错性
数据自动保存多个副本。它通过增加副本的形式,提高容错性。某一个副本丢失后,可以自动恢复。
2)适合处理大数据
1、数据规模:能够处理GB、TB、甚至PB级别的数据。
2、文件规模:能够处理百万规模以上的文件数量。
3)可构建在廉价机器上,通过多副本机制,提高可靠性。
HDFS缺点
1)不适合低延时数据访问,比如毫秒级的存储数据。
2)无法高效的对大量小文件进行存储。
1、存储大量小文件的话,会占用NameNode大量内存来储存文件目录和快信息。
2、小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
3)不支持并发写入、文件随机修改。
1、一个文件只能有一个写,不允许多个线程同时写。
2、仅支持数据append(追加),不支持文件的随机修改。
HDFS组成架构
HDFS文件块大小**
为什么块大小不能设置太小,也不能设置太大?
1、HDFS块大小设置太小,会增加寻址时间,程序一直在找块的开始位置。
2、如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢。
总结:HDFS的块大小设置主要取决于磁盘传输速率。
(可通过修改配置hdfs-site.xml参数dfs.blocksize(默认值134217728即128m)来修改块大小)
HDFS常用命令(linux)
基本语法
#两个命令相同
bin/hadoop fs xxx
bin/hdfs dfs xxx
命令大全
#可通过hadoop fs -help xx查看对应命令详情
Usage: hadoop fs [generic options]
[-appendToFile <localsrc> ... <dst>]
[-cat [-ignoreCrc] <src> ...]
[-checksum <src> ...]
[-chgrp [-R] GROUP PATH...]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] <localsrc> ... <dst>]
[-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] <path> ...]
[-cp [-f] [-p | -p[topax]] [-d] <src> ... <dst>]
[-createSnapshot <snapshotDir> [<snapshotName>]]
[-deleteSnapshot <snapshotDir> <snapshotName>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] [-v] [-x] <path> ...]
[-expunge]
[-find <path> ... <expression> ...]
[-get [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-getmerge [-nl] [-skip-empty-file] <src> <localdst>]
[-head <file>]
[-help [cmd ...]]
[-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [<path> ...]]
[-mkdir [-p] <path> ...]
[-moveFromLocal <localsrc> ... <dst>]
[-moveToLocal <src> <localdst>]
[-mv <src> ... <dst>]
[-put [-f] [-p] [-l] [-d] <localsrc> ... <dst>]
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ...]
[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
[-setfattr {-n name [-v value] | -x name} <path>]
[-setrep [-R] [-w] <rep> <path> ...]
[-stat [format] <path> ...]
[-tail [-f] [-s <sleep interval>] <file>]
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touch [-a] [-m] [-t TIMESTAMP ] [-c] <path> ...]
[-touchz <path> ...]
[-truncate [-w] <length> <path> ...]
[-usage [cmd ...]]
常用命令
上传
#从本地剪切粘贴到HDFS
-moveFromLocal <localsrc> ... <dst>
#从本地拷贝粘贴到HDFS
-copyFromLocal <localsrc> ... <dst>
#追加一个文件到已经存在的文件末尾
-appendToFile <localsrc> ... <dst>
#等同于copyFromLocal
-put <localsrc> ... <dst>
下载
#从HDFS拷贝到本地
-copyToLocal <src> ... <localdst>
#等同于copyToLocal
-get <src> ... <localdst>
#合并下载多个文件(注意该命令全小写)
-getmerge <src> ... <localdst>
其他文件操作
#等同于liunx
-ls
-mkdir
-cat
-chgrp、-chmod、-chown
-cp
-mv
-tail
-rm
-rmdir
#统计文件夹大小
-du [-s 合并显示总大小] [-h 格式化显示大小单位] [-v] [-x] <path> ...
#设置HDFS中文件的副本数量(若文件副本数量大于节点数量,会在新增加节点时自动增加副本)
-setrep <rep 数量> <path 文件路径> ...
HDFS客户端操作(Windows)**
HDFS客户端环境准备
#环境变量
依赖文件:hadoop-3.1.0
配置HADOOP_HOME环境变量
#若有问题可将hadoop-3.1.0的bin目录下的hadoop.dll和winutils.exe放到C:/windows/system32目录下。
HDFS配置MAVEN工程依赖
创建一个Maven工程并添加依赖坐标(pom.xml)
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
在src/main/resources目录下新建文件log4j2.xml
HDFS的API操作
创建文件系统对象
// 方式一:
/*
uri : HDFS的地址(NameNode的地址)
conf : 配置内容
user : 操作集群的用户
*/
FileSystem fs = FileSystem.get(final URI uri, final Configuration conf,final String user)
// 方式二:
/*
1.需要在Configuration中配置HDFS的路径
2.先运行一下该类(否则找不到)
选中对应类的配置 ----> ditConfigurations ----> VM Options ---->填入-DHADOOP_USER_NAME=atguigu
*/
Configuration conf = new Configuration();
//需要配置HDFS的地址
conf.set("fs.defaultFS","hdfs://hadoop102:9820");
FileSystem fs = FileSystem.get(conf);
// 拓展:main(String[] args)方法参数由控制台传递,也可在idea中edit configurations中的program arguments中设置,参数由空格隔开。
// 控制台: java xxx.class(字节码文件) arg1 arg2 ...
// idea:program arguments[ arg1 arg2 ...]
常用文件系统操作
上传
/*
delSrc : 是否删除源文件(本地)
overwrite : 如果目标文件已经存在是否覆盖目标文件(HDFS)如果为false : 如果目标文件存在则报错
src : 源文件路径(本地)
dst : 目标文件路径(HDFS)
*/
fs.copyFromLocalFile((boolean delSrc, boolean overwrite,Path src, Path dst)
下载
/*
delSrc : 是否删除源文件(HDFS)
src :源文件路径(HDFS)
dst : 目标文件路径(本地)
useRawLocalFileSystem :是否使用RawLocalFileSystem
*/
fs.copyToLocalFile(boolean delSrc, Path src, Path dst,boolean useRawLocalFileSystem)
删除文件夹
/*
f : 要删除的文件和目录的路径
recursive : 是否递归删除
注意:如果是删除目录的话该值只能为true否则报错
如果是文件(空目录)该值既可以为true也可以为false
*/
fs.delete(Path f, boolean recursive)
修改文件名
/*
src : 源文件路径(HDFS)
dst : 目标文件路径(HDFS)
*/
fs.rename(Path src, Path dst)
// 修改文件名
fs.rename(new Path("/aaa.txt"),new Path("/ccc.txt"));
// 移动文件
fs.rename(new Path("/ccc.txt"),new Path("/output"));
查看文件详情
/*
f : 是文件或目录的路径
recursive :是否递归
*/
RemoteIterator<LocatedFileStatus> remoteIterator = fs.listFiles(final Path f, final boolean recursive)
// 遍历
while(remoteIterator.hasNext()){
//获取文件
LocatedFileStatus fileStatus = remoteIterator.next();
System.out.println("====================" + fileStatus.getPath().getName()+ "=====");
System.out.println("文件名:" + fileStatus.getPath().getName());
System.out.println("副本数" + fileStatus.getReplication());
//获取块信息
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
System.out.println(Arrays.toString(blockLocations));
}
查看文件的类型
/*
f : 目标文件或目录的路径
*/
FileStatus[] status = this.fs.listStatus(Path f);
//遍历
for (FileStatus f : status) {
if (f.isFile()){
System.out.println(f.getPath().getName() + "是一个文件");
}else if (f.isDirectory()){
System.out.println(f.getPath().getName() + "是一个目录");
}
}
使用流上传或下载文件
上传
//输入流 - 文件输入流
FileInputStream fis = new FileInputStream("D:\io\hdfs\aa.txt");
//输出流 - 向HDFS上输出的流
FSDataOutputStream fos = fs.create(new Path("/aa.txt"));
//一边读一边写
/*
copyBytes(InputStream in, OutputStream out,int buffSize, boolean close)
in :输入流
out :输出流
buffsize :缓冲区大小
close : 是否关闭流
*/
IOUtils.copyBytes(fis,fos,1024,false);
//关流
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
下载
//输入流 - 从HDFS上输入的流
FSDataInputStream fis = fs.open(new Path("/aa.txt"));
//输出流 - 文件输出流
FileOutputStream fos = new FileOutputStream("D:\io\hdfs\aa.txt");
//一边读一边写(文件对拷)
IOUtils.copyBytes(fis,fos,1024,true);
配置的优先级
配置的位置
-
xxx.default.xml
-
xxx.site.xml
-
maven工程src/main/resources中xxx.site.xml
-
代码中Configuration做配置
配置的优先级
4 > 3 > 2 > 1
HDFS读写数据流程**
HDFS写数据流程
-
Client创建FileSystem对象,向NameNode请求上传文件,NameNode检查文件是否存在,文件目录是否存在,判断用户是否有权限
-
NameNode响应可以上传
-
Client切分文件块,并向NameNode上传第一个Block,请求NameNode返回DataNode节点
-
NameNode返回dfs.replication数量的节点
-
Client创建输出流,向dn1请求建立Block传输通道,成功后继续向dn2请求,成功后向dn3请求
-
dn3,dn2,dn1返回成功应答
-
Client向dn1以数据包Package的形式上传数据,dn1往内存中Bytebuffer写入的同时,写到dn1本地磁盘,同时内存中Bytebuffer向dn2的Bytebuffer中上传...
// Package传输时放入应答队列等待应答
// 若传输途中有dn节点宕机,则会跳过此节点,传输完成后,NameNode重新制定dn存储副本,该节点重启后,删除未完成的传输内容
节点距离计算与机架感知
2.x版本:在同一机架上选择第二个节点
3.x版本:在不同机架上选择第二个节点,并在考虑到效率问题,在第二个节点所在机架上选择第三个节点,若节点数超过3个,则完全随机。
HDFS读数据流程
-
Client创建FileSystem对象,向NameNode请求下载文件,NameNode判断是否有该文件,判断该用户是否有权限
-
NameNode返回目标文件的元数据
-
Client创建流,挑选一台DataNode(就近原则,然后随机)逐个找DataNode读取数据块
-
DataNode从磁盘读取数据输入流,以Packet为单位做校验
-
Client以Packet为单位接收数据,先在本地缓存,然后写入文件
NameNode和SecondaryNameNode**
NN和2NN工作机制(CheckPoint)
-
Client发出请求
-
Namenode收到请求后,将命令记录到滚动日志edits_inprogress_xx,并在内存中执行该命令
// NameNode启动时,会将edits_inprogress_xx和fsimage读到内存
-
2NN不停向NN询问是否需要CheckPoint(配置在hdfs.default.xml中)
// dfs.namenode.checkpoint.check.period 默认为60s
当CheckPoint条件触发(配置在hdfs.default.xml中)
// dfs.namenode.checkpoint.period 默认为3600s
// dfs.namenode.checkpoint.txns 默认为1000000
-
NameNode中将edits_inprogress_xx文件重命名为edits_xxx并且新建一个edits_inprogress_xx2文件继续存放滚动日志
-
将重命名后的edits_xxx文件及fsimage文件从磁盘拷贝到2NN,2NN会在内存中执行edits_xxx生成元数据并与fsimage中的元数据合并,生成文件fsimage_chkpoint
-
将2NN中fsimage_chkpoint拷贝到NN中,并重命名为fsimage,至此一个CheckPoint完成
拓展:
若NameNode故障,可紧急将2NN的数据(/opt/module/hadoop-3.1.3/data/tmp/dfs/name)拷贝到NameNode对应目录中,重启NameNode即可。
集群安全模式
NameNode启动
NameNode启动时,将Fsimage文件加载进内存,并执行Edits_inprogress_xx中的操作,在内存中建立元数据映像成功后,将创建新的Fsimage和一个空的滚动日志编辑文件,然后开始监听DataNode请求,过程中,NameNode一直处于安全模式,NameNode的文件对客户端说是只读的。
DataNode启动
HDFS系统中数据块的位置信息并不是由NameNode维护的,而是以块列表的形式存在DataNode中,在系统正常操作期间,NameNode会在内存中保留所有的块位置的信息。在安全模式下,各个DataNode会向NameNode发送最新的块列表信息,NameNode了解到足够多的快信息后,即可高效运行文件系统。
安全模式退出判断
最小副本条件:hdfs.default.xml 中
dfs.namenode.replication.min 默认值为1
如果满足最小副本条件,NameNode会在30秒之后退出安全模式。最小副本条件是指99.9%的块满足最小副本级别。在启动一个刚刚格式化的HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式。
#安全模式命令
#查看安全模式状态
bin/hdfs dfsadmin -safemode get
#进入安全模式
bin/hdfs dfsadmin -safemode enter
#离开安全模式
bin/hdfs dfsadmin -safemode leave
#等待安全模式结束
bin/hdfs dfsadmin -safemode wait
DataNode工作机制**
DataNode启动后,向NameNode注册,报告该节点所存放的块信息,并且每周期(1小时)上报一次所有块信息。
DN心跳机制
心跳间隔:hdfs-default.xml
dfs.heartbeat.interval 默认为3s
Timeout计算公式:
Timeout=2*dfs.namenode.heartbeat.recheck-interval+10*dfs.heartbeat.interval
dfs.namenode.heartbeat.recheck-interval 默认为300000ms(5分钟)
dfs.heartbeat.interval 默认为3s
DataNode每3秒向NameNode发送心跳以证明该节点存活,心跳返回的结果带有NameNode给DataNode的命令,若NameNode超过10分钟30秒没收到某节点的心跳,则认为该节点不可用。
DN数据完整性
-
DataNode读取Block的时候,会计算CheckSum
-
如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏
-
Client读取其他DataNode上的Block
-
DataNode在其文件创建后周期验证CheckSum
数据传递时是以Package包形式,包中携带chunk(由512字节+4字节(32位))作为检验和
服役新数据节点
#配置hostname
sudo vim /etc/hostname
#配置hosts
sudo vim /etc/hosts
#配置hadoop配置文件(使用同步脚本)
#直接启动DataNode即可关联到集群
hdfs --daemon start datanode
#如果数据不均衡,可以使用命令实现集群的再平衡
$HADOOP_HOME/sbin/start-balancer.sh
#补充:$HADOOP_HOME/etc/hadoop/workers中配置的是$HADOOP_HOME/sbin/start-dfs.sh脚本使用的集群配置,单独添加一台DN节点,只需配置好NameNode节点信息,就可加入集群
#注意:修改hostname需重启linux
白名单与黑名单
白名单
-
配置NameNode中hdfs-site.xml
<!-- 增加dfs.hosts属性 在目录创建对应文件 -->
<property>
<name>dfs.hosts</name>
<value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts</value>
</property>
-
文件添加白名单节点(末行不得有空行)
hadoop102
hadoop103
hadoop104
-
分发配置
xsync hdfs-site.xml
-
刷新NameNode
hdfs dfsadmin -refreshNodes
-
更新ResourceManager节点
yarn rmadmin -refreshNodes
-
如果数据不均衡,可以用命令实现集群的再平衡
$HADOOP_HOME/sbin/start-balancer.sh
注意:直接移除白名单并刷新节点,会导致被移除的节点DataNode进程直接关闭,无法实行退役操作,即该节点数据无法迁移到别的节点。
黑名单
-
配置NameNode的hdfs-site.xml配置文件
<!-- 增加dfs.hosts.exclude属性 在目录创建对应文件 -->
<property>
<name>dfs.hosts.exclude</name>
<value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts.exclude</value>
</property>
-
文件添加黑名单节点(末行不得有空行)
hadoop105
-
刷新NameNode
hdfs dfsadmin -refreshNodes
-
更新ResourceManager节点
yarn rmadmin -refreshNodes
-
检查web(hadoop102:9870),该节点状态为decommission in progress(退役中),说明该阶段数据块正复制到其他节点。
-
等待节点状态为decommissioned,表示已经复制完,停止该节点资源管理器。
-
如果数据不均衡,可以用命令实现集群的再平衡
$HADOOP_HOME/sbin/start-balancer.sh
注意:如果退役会导致服役副本节点数小于配置副本数,则不能退役成功,需修改副本数。
DataNode多目录配置
DataNode也可以配置成多个目录存储数据,每个目录存储的数据不一样。即:数据不是副本。使用场景为增加硬盘时,扩容。
<!-- 配置后根据情况是否分发,重启该DN节点,会自动创建文件夹 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
</property>