Hadoop(HDFS)
1.HDFS概述
HDFS(Hadoop Distributed File System),是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
HDFS的使用场景:适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后就不需要改变。
1.1 HDFS优点
- 高容错性
- 数据自动保存多个副本,某一个副本丢失,不影响程序正常运行,且能够自动恢复。
- 适合处理大数据
- 适合处理GB/TB/PB级别的数据。
- 能够出来百万规模以上的文件数量。
- 可构建在普通机器上,通过集群方式提高处理性能,不必要很昂贵的机器。
1.2 HDFS缺点
- 不适用低延时数据访问
- 比如毫秒级的存储数据,HDFS做不到
- 无法高效的对大量小文件进行存储
- 存储大量小文件的话,会占用NameNode大量内存来存储这些小文件的文件目录及存储块信息。
- 大量小文件的寻址时间会超过读取时间。
- 不支持并发写入、文件随机修改。
- 一个文件只能有一个线程写,不允许多个线程同时写。
- 仅支持数据在文件末尾追加,不支持文件在随机位置的修改。
1.3 HDFS组成架构


2.HDFS的shell操作
2.1 基本语法
hadoop fs 具体命令 OR hdfs dfs 具体命令 两个是完全相同的。
2.2 命令大全
bin/hadoop fs
[-appendToFile <localsrc> ... <dst>]
[-cat [-ignoreCrc] <src> ...]
[-chgrp [-R] GROUP PATH...]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-copyFromLocal [-f] [-p] <localsrc> ... <dst>]
[-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-count [-q] <path> ...]
[-cp [-f] [-p] <src> ... <dst>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] <path> ...]
[-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-getmerge [-nl] <src> <localdst>]
[-help [cmd ...]]
[-ls [-d] [-h] [-R] [<path> ...]]
[-mkdir [-p] <path> ...]
[-moveFromLocal <localsrc> ... <dst>]
[-moveToLocal <src> <localdst>]
[-mv <src> ... <dst>]
[-put [-f] [-p] <localsrc> ... <dst>]
[-rm [-f] [-r|-R] [-skipTrash] <src> ...]
[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
<acl_spec> <path>]]
[-setrep [-R] [-w] <rep> <path> ...]
[-stat [format] <path> ...]
[-tail [-f] <file>]
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
2.2 常用命令实操
创建文件夹
# 创建文件夹之前查看根目录
[atguigu@hadoop102 bin]$ hadoop fs -ls /
Found 3 items
drwx------ - atguigu supergroup 0 2022-10-28 16:12 /tmp
drwxr-xr-x - atguigu supergroup 0 2022-10-20 16:40 /wcinput
drwxr-xr-x - atguigu supergroup 0 2022-10-28 16:13 /wcoutput
# 创建hdfs集群文件夹
[atguigu@hadoop102 bin]$ hadoop fs -mkdir /dnf
# 创建文件夹之后查看根目录
[atguigu@hadoop102 bin]$ hadoop fs -ls /
Found 4 items
drwxr-xr-x - atguigu supergroup 0 2022-11-02 16:03 /dnf
drwx------ - atguigu supergroup 0 2022-10-28 16:12 /tmp
drwxr-xr-x - atguigu supergroup 0 2022-10-20 16:40 /wcinput
drwxr-xr-x - atguigu supergroup 0 2022-10-28 16:13 /wcoutput
文件上传及下载
# 将服务器本地文件复制拷贝到集群文件夹(copyFromLocal和put功能相同)
hadoop fs -copyFromLocal mytest.txt /dnf
hadoop fs -put mytest.txt /dnf
# 将服务器本地文件剪切拷贝到集群文件夹
hadoop fs -moveFromLocal mytest.txt /dnf
# 将一个新文件追加到之前文件末尾
hadoop fs -appendToFile mytest2.txt /dnf/mytest.txt
# 将文件从hdfs下载到本地服务器(copyToLocal和get功能相同)
hadoop fs -copyToLocal /dnf/mytest.txt ./
hadoop fs -get /dnf/mytest.txt ./
hdfs其他操作
# -ls: 显示目录信息
hadoop fs -ls /dnf
# -cat:显示文件内容
hadoop fs -cat /dnf/mytest.txt
# -chgrp、-chmod、-chown:Linux文件系统中的用法一样,修改文件所属权限
hadoop fs -chmod 666 /dnf/mytest.txt
hadoop fs -chown root:root /dnf/mytest.txt
# -cp:从HDFS的一个路径拷贝到HDFS的另一个路径
hadoop fs -cp /dnf/mytest.txt /lol
# -mv:在HDFS目录中移动文件
hadoop fs -mv /dnf/mytest.txt /lol
# -tail:显示一个文件的末尾1kb的数据
hadoop fs -tail /dnf/mytest.txt
# -rm:删除文件或文件夹
hadoop fs -rm /dnf/mytest.txt
# -rm -r:递归删除目录及目录里面内容
hadoop fs -rm -r /dnf
# -du统计文件夹的大小信息
hadoop fs -du -s -h /wcinput
# 27 81 /wcinput 27表示该文件夹所有文件大小 81表示该文件夹所有副本文件大小。81=27*3
# 统计每个文件的大小信息
hadoop fs -du -h /wcinput
14 42 /wcinput/test1.txt
7 21 /wcinput/test2.txt
6 18 /wcinput/test3.tx
3 HDFS的API案例实操
新建一个maven项目
- 引入依赖
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>
log4j配置文件
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
3.1 创建连接集群的客户端
- 注:新建客户端时需要指定linux用户。
- 客户端去操作HDFS时,是有一个用户身份的。默认情况下,HDFS客户端API会从采用Windows默认用户访问HDFS,会报权限异常错误。所以在访问HDFS时,一定要配置用户。
public class HdfsClient {
@Test
public void testMkdir() throws URISyntaxException, IOException, InterruptedException {
//1.获取文件系统
Configuration configuration = new Configuration();
FileSystem fileSystem = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "atguigu");
//2.创建目录
fileSystem.mkdirs(new Path("/lol"));
//3.关闭资源
fileSystem.close();
}
}
3.2 通过API上传文件
@Test
public void testUpload() throws URISyntaxException, IOException, InterruptedException {
//1.创建客户端
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "atguigu");
/*
第一个参数:如果文件已存在,是否覆盖,true:覆盖 false:不覆盖
第二个参数:本地文件路径
第三个参数:需要上传到集群的目录
*/
fs.copyFromLocalFile(false,new Path("D:\\logs\\sys-error.log"),new Path("/lol/logs"));;
//3. 关闭资源
fs.close();
}
3.3 通过API下载文件到本地
@Test
public void testDownload() throws URISyntaxException, IOException, InterruptedException {
//1.创建客户端
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "atguigu");
/*
第一个参数 boolean delSrc 指是否将原文件删除
第二个参数 Path src 指要下载的文件路径
第三个参数 Path dst 指将文件下载到的路径
第四个参数 boolean useRawLocalFileSystem 是否开启文件校验
*/
fs.copyToLocalFile(false,new Path("/lol/logs"),new Path("D:/logs.txt"),true);
//3. 关闭资源
fs.close();
}
3.4 文件更名和移动
fs.rename(new Path("/xiyou/huaguoshan/sunwukong.txt"), new Path("/xiyou/huaguoshan/meihouwang.txt"));
3.4 删除文件或目录
fs.delete(new Path("/xiyou"), true);
3.5 查看文件详情信息
// 2 获取文件详情
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
while (listFiles.hasNext()) {
LocatedFileStatus fileStatus = listFiles.next();
System.out.println("========" + fileStatus.getPath() + "=========");
System.out.println(fileStatus.getPermission());
System.out.println(fileStatus.getOwner());
System.out.println(fileStatus.getGroup());
System.out.println(fileStatus.getLen());
System.out.println(fileStatus.getModificationTime());
System.out.println(fileStatus.getReplication());
System.out.println(fileStatus.getBlockSize());
System.out.println(fileStatus.getPath().getName());
// 获取块信息
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
System.out.println(Arrays.toString(blockLocations));
}

3.6 判断是文件还是文件夹
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for (FileStatus fileStatus : listStatus) {
// 如果是文件
if (fileStatus.isFile()) {
System.out.println("f:"+fileStatus.getPath().getName());
}else {
System.out.println("d:"+fileStatus.getPath().getName());
}
}
4. HDFS的读写流程(面试重点)
4.1 HDFS的写数据流程

(1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
(2)NameNode返回是否可以上传。
(3)客户端请求第一个 Block上传到哪几个DataNode服务器上。
(4)NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
(5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
(6)dn1、dn2、dn3逐级应答客户端。
(7)客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
(8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
4.2 HDFS读数据流程

(1)客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
(2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
(4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
5. NameNode和SecondaryNameNode
5.1 NN和2NN工作机制
思考:NameNode中的元数据是怎么存储的?
- 首先,我们做个假设,如果存储在NameNode节点的磁盘中,因为经常需要进行随机访问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数据的FsImage。
- 这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦NameNode节点断电,就会产生数据丢失。因此,引入Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到Edits中。这样,一旦NameNode节点断电,可以通过FsImage和Edits的合并,合成元数据。
- 但是,如果长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并。
NameNode工作机制

1)第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode记录操作日志,更新滚动日志。
(4)NameNode在内存中对元数据进行增删改。
2)第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
(2)Secondary NameNode请求执行CheckPoint。
(3)NameNode滚动正在写的Edits日志。
(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件fsimage.chkpoint。
(7)拷贝fsimage.chkpoint到NameNode。
(8)NameNode将fsimage.chkpoint重新命名成fsimage。
5.2 Fsimage(镜像文件)和Edits(编辑日志)解析


查看Fsimage(镜像文件)和Edits(编辑日志)
- 如果直接通过cat命令查看这两个文件的话,会产生乱码,需要使用专门的命令来查看。
1)oiv查看Fsimage文件
基本语法
- hdfs oiv -p 文件类型 -i镜像文件 -o 转换后文件输出路径
hdfs oiv -p XML -i fsimage_0000000000000000523 -o /opt/module/fsimage.xml

<inode>
<id>16386</id>
<type>DIRECTORY</type>
<name>user</name>
<mtime>1512722284477</mtime>
<permission>atguigu:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16387</id>
<type>DIRECTORY</type>
<name>atguigu</name>
<mtime>1512790549080</mtime>
<permission>atguigu:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16389</id>
<type>FILE</type>
<name>wc.input</name>
<replication>3</replication>
<mtime>1512722322219</mtime>
<atime>1512722321610</atime>
<perferredBlockSize>134217728</perferredBlockSize>
<permission>atguigu:supergroup:rw-r--r--</permission>
<blocks>
<block>
<id>1073741825</id>
<genstamp>1001</genstamp>
<numBytes>59</numBytes>
</block>
</blocks>
</inode >
结论
- fsimage文件记录了HDFS各个文件夹(名称,大小,所属用户)及文件(块大小,副本数)的信息,及其目录的父子关系。
- 没有记录各个文件块对应的DataNode所在位置。
- 在集群启动后,要求DataNode上报数据块信息,并间隔一段时间后再次上报。
2)oev查看Edits文件
基本语法
- hdfs oev -p 文件类型 -i编辑日志 -o 转换后文件输出路径
hdfs oev -p XML -i edits_inprogress_0000000000000000524 -o /opt/module/edit4.xml
结论
- edits文件记录了对hdfs的每一步操作步骤,只在最末尾进行追加。
- NameNode如何确定下次开机启动的时候合并哪些Edits
-
- 每隔一小时进行镜像文件和编辑日志合并,只合并大于镜像文件末尾数字的编辑日志。
-
- 每次开机启动时合并。
-
6. DataNode
DataNode工作机制

(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
(2)DataNode启动后向NameNode注册,通过后,周期性(6小时)的向NameNode上报所有的块信息。
<--!DN向NN汇报当前解读信息的时间间隔,默认6小时-->
<property>
<name>dfs.blockreport.intervalMsec</name>
<value>21600000</value>
<description>Determines block reporting interval in milliseconds.</description>
</property>
<--!DN扫描自己节点块信息列表的时间,默认6小时-->
<property>
<name>dfs.datanode.directoryscan.interval</name>
<value>21600s</value>
<description>Interval in seconds for Datanode to scan data directories and reconcile the difference between blocks in memory and on the disk.
Support multiple time unit suffix(case insensitive), as described
in dfs.heartbeat.interval.
</description>
</property>
(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
(4)集群运行中可以安全加入和退出一些机器。
7.数据完整性
如下是DataNode节点保证数据完整性的方法。
(1)当DataNode读取Block的时候,它会计算CheckSum。
(2)如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
(3)Client读取其他DataNode上的Block。
(4)常见的校验算法crc(32),md5(128),sha1(160)
(5)DataNode在其文件创建后周期验证CheckSum。


浙公网安备 33010602011771号