本文内容均来自于尚硅谷Hadoop3.0课程,感兴趣的同学可以在B站找到官方的视频教程哈
1、概述
1.1、产生背景和定义
- 背景:一个节点无法装下所有的数据,因此将大数据分散到不同节点上存储,需要分布式文件管理系统管理这些分布式文件,HDFS就是分布式文件管理系统的一种实现。
- 定义:分布式文件管理系统,通过目录树定位文件
- 使用场景:单词写入,多次读取(无法update已经上传的数据,只能append)
1.2、优缺点
-
高容错性
- 数据自动保存多个副本。它通过增加副本的形式,提高容错性。
- 一个副本丢失后,可以自动恢复
-
适合处理大数据
- 数据规模GB、TB、甚至PB
- 文件规模能够高达百万以上
-
可构建在廉价机器上,通过多副本机制,提高可靠性
-
不适合低延迟数据访问,比如毫秒级别访问
-
无法高效对大量小文件存储,NameNode(内存中)会存储文件目录和块信息,大量小文件说明内存中将会存储大量文件目录和块信息,不可取。小文件的寻址时间超过读取时间。
-
不支持并发写入一个文件。仅支持append,不支持update
1.3、组成
- 官网架构图
-
NameNode(NN):就是master,管理datanode集群信息
- 管理HDFS的名称空间、副本策略(一主多从类似的)、数据块的映射信息(某些数据块在某个datanode上),处理客户端读写请求
-
DataNode(DN):就是slaver,存储数据、执行读写IO操作
-
client:客户端
- 文件切分,文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传
- 与NN交互,获取DN地址
- 与DN进行IO交互
- Client提供一些命令来管理HDFS,比如NameNode格式化
- Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作
-
Second NameNode(2NN):并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。
- 辅助NameNode,分担其工作量,比如定期合并Fsimage(镜像文件)和Edits,并推送给NameNode;
- 在紧急情况下,可辅助恢复NameNode(仅能恢复一部分数据)。
1.4、文件块大小
HDFS中的文件在物理上是分块存储,块大小可通过dfs.blocksize参数设置,2/3版本后默认128M。
寻址时间为传输时间的1%最佳:寻址时间10ms,传输时间1s,磁盘传输效率大约100M/s,因此设置128M差不多
为什么块的大小不能设置太小,也不能设置太大?
- 块太小,则相对块的数量非常多,寻址时间增加
- 块太大,传输时间则增加,同时处理数据非常耗时
HDFS块的大小设置主要取决于磁盘传输速率。
2、HDFS的shell操作
hadoop fs OP === hdfs dfs OP 大部分命令和Linux很像,例如mkdir、ls、cat等
- 帮助命令
# hadoop fs -help op
hadoop fs -help mkdir
2.1、上传
- -moveFromLocal:本地文件剪切至HDFS
# hadoop fs -moveFromLocal 本地文件路径 hdfs文件路径
hadoop fs -moveFromLocal ./test.txt /test
- -copyFromLocal:本地文件拷贝至HDFS
# hadoop fs -copyFromLocal 本地文件路径 hdfs文件路径
hadoop fs -copyFromLocal ./test.txt /test
- -put:等同于-copyFromLocal
- -appendToFile:将本地文件追加至HDFS文件上
# hadoop fs -appendToFile 本地文件路径 hdfs文件路径
hadoop fs -appendToFile ./test.txt /test
2.2、下载
- -copyToLocal:从HDFS拷贝至本地
# hadoop fs -copyToLocal hdfs文件路径 本地文件路径
hadoop fs -copyToLocal /test/test.txt ./
- -get:等同于-copyToLocal
3、HDFS的API操作
3.1、客户端创建
URI uri = new URI("hdfs://hadoop100:8020");
// 配置参数优先级:hdfs-default.xml => hdfs-site.xml => 在项目资源目录下的配置文件 => 代码里面的配置
Configuration configuration = new Configuration();
configuration.set("dfs.replication", "2");
String user = "chub";
FileSystem fs = FileSystem.get(uri, configuration, user);
3.2、常用API
- 创建目录
fs.mkdirs(new Path("/dir/subdir1"));
- 上传文件
// 参数解读:参数一:表示删除原数据; 参数二:是否允许覆盖;参数三:原数据路径; 参数四:目的地路径
fs.copyFromLocalFile(false, true, new Path("D:\test.txt"), new Path("hdfs://hadoop100/dir/subdir1"));
FSDataOutputStream fos = fs.create(new Path("/input"));
fos.write("hello world".getBytes());
- 下载文件
// 参数的解读:参数一:原文件是否删除;参数二:原文件路径HDFS; 参数三:目标地址路径Win ; 参数四:
//fs.copyToLocalFile(true, new Path("hdfs://hadoop100/dir/subdir1/"), new Path("D:\"), true);
fs.copyToLocalFile(false, new Path("hdfs://hadoop100/test.txt"), new Path("D:\"), false);
- 文件删除
// 参数解读:参数1:要删除的路径; 参数2 : 是否递归删除
// 删除文件
fs.delete(new Path("/jdk-8u212-linux-x64.tar.gz"), false);
// 删除空目录
fs.delete(new Path("/dir2"), false);
// 删除非空目录
fs.delete(new Path("/dir"), true);
- 文件rename和移动
// 参数解读:参数1 :原文件路径; 参数2 :目标文件路径
// 对文件名称的修改
fs.rename(new Path("/dir/test.txt"), new Path("/dir/test1.txt"));
// 文件的移动和更名
fs.rename(new Path("/dir/test.txt"), new Path("/dir2/test1.txt"));
// 目录更名
fs.rename(new Path("/dir"), new Path("/dir1"));
- 获取文件详情
// 获取所有文件信息
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));
}
- 判断文件或者文件夹
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for (FileStatus status : listStatus) {
if (status.isFile()) {
System.out.println("文件:" + status.getPath().getName());
} else {
System.out.println("目录:" + status.getPath().getName());
}
}
4、HDFS的读写流程
4.1、HDFS的写数据流程
-
客户端侧:创建HDFS客户端(Distributed FileSystem)
-
NameNode侧:检查目录树是否可以创建文件
- 检查权限
- 检查目录结构(目录是否存在)
-
NN侧:响应允许上传文件
-
客户端侧:请求DN节点地址
-
NN侧:返回DN节点地址(返回最近节点距离的节点)
-
客户端侧:创建数据流,向某一个DN节点建立连接,然后该DN节点与其它DN节点建立连接,传输数据
4.2、网络拓扑-节点距离计算
HDFS写入数据时,会将数据上传至最近节点距离的DN,这个距离的计算方式如下:
节点距离:两个节点到达最近的共同祖先的距离总和。
- 同一节点上的进程 = 0
- 同一机架上的不同节点 = 2
- 同一数据中心不同机架上的节点 = 4
- 不同数据中心的节点 = 6
4.3、机架感知(副本节点选择)
第一个副本在Client所处的节点上。如果客户端在集群外,随机选一个。
第二个副本在另一个机架的随机一个节点
第三个副本在第二个副本所在机架的随机节点
4.4、HDFS读数据流程
- 客户端侧:创建HDFS客户端(Distributed FileSystem)
- 客户端侧:请求下载文件
- NN侧:返回目标文件的元数据信息
- 客户端侧:创建数据流,根据节点距离和节点的当前负载能力选择读取的节点
5、NN和2NN的工作机制
fsimage文件:镜像文件,主要保存元数据信息
edits文件:镜像文件的操作记录文件,存储的是对镜像文件的操作记录。类似Redis中的AOF
5.1、NN和2NN工作原理
NN启动后会加载fsimage和edits到内存中。每次有元数据修改,首先写edits文件,记录操作步骤。然后2NN会同步edits和fsimage,生成新的fsimage文件,该文件的元数据是包含启动后的元数据+启动后更新的步骤。最后将新生成的fsimage同步至NN上。保证内存中的数据为最新的。
5.2、fsimage和edits解析
Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息。
Edits.文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先 会被记录到Edits文件中。
seen_txid文件保存的是一个数字,就是最后一个edits的数字
每次NameNode启动的时候都会将Fsimage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimagei和Edits文件进行了合并。
- 查看Fsimage文件
通过如下命令可以解析fsimage镜像文件
hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径
- 查看edits文件
通过如下命令可以解析edits文件
hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径
- checkpoint时间设置
通常情况下,2NN每隔1小时进行checkpoint一次,同步edits和fsimage。
dfs.namenode.checkpoint.period 参数可设置
通常情况下,2NN每当操作次数达到1百万时,执行一次checkpoint
dfs.namenode.checkpoint.txns 参数可设置
默认情况下,2NN每隔60s检查NN的操作次数
dfs.namenode.checkpoint.check.period 参数可设置
6、DN工作机制
DN启动后,向NN注册
注册成功,NN保存DN元数据
DN每隔6h向NN上报数据块信息、每隔3s上报心跳。同时心跳会携带NN给DN的命令。
NN在10min+30s内没有收到DN心跳,认为DN已下线
6.1、DN数据完整性
- 当DataNode读取Block的时候,它会计算CheckSum
- 如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
- Client读取其他DataNode上的Block
- 常见的校验算法crc(32),md5(128),sha1(160)
- DataNode在其文件创建后周期验证CheckSum。