第一章 HDFS概述
1.1 HDFS产出背景及定义
1.1.1 HDFS背景
- 随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种。
1.1.2 HDFS定义
- HDFS,是一个文件系统,用于存储文件,通过目录树来定位文件,其次,它是分布式的,由很多服务器联合起来实现其功能,其群众的服务器有各自的角色。
- HDFS的使用场景:适合一次写入,多次读出的场景,且不支持文件的修改。适合用来作数据分析,并不适合用来作网盘应用。
1.2 HDFS的优缺点
1.2.1 优点
- 高容错性
- 数据自动保存多个副本,它通过增加副本的形式,提供容错性
- 某一个副本丢失后,它可以自动恢复
- 适合处理大数据
- 数据规模:能够处理数据规模达到GB、TB、甚至PB的数据
- 文件规模:能够处理百万规模以上的文件数量,数量相当之大
- 可以构建在廉价机器上,通过多副本机制,提高可靠性
1.2.2 缺点
- 不合适低延时数据访问,比如毫秒级的存储数据,是做不到的
- 无法高效的对大量的小文件进行存储
- 存储大量的小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的
- 小文件存储的寻址时间会超过读取时间,他违反了HDFS的设计目标
- 不支持并发写入、文件随机修改
- 一个文件只能有一个写,不允许多个线程同时写
- 仅支持数据append(追加),不支持文件的随机修改
1.3 HDFS组成架构
- NameNode:就是Master,它是一个主管,管理者
- 管理HDFS的名称空间
- 配置副本策略
- 管理数据块(Block)映射信息
- 处理客户端读写数据
- DataNode:就是Slave。NameNode下达命令,DataNode执行实际操作
- 存储实际的数据块
- 执行数据块的读/写操作
1.4 HDFS文件块大小
- HDFS中文件在物理上是分块存储(Block),块的大小可以通过配置参数(dfs.blocksize)来规定,默认大小在Hadoop2.x版本中是128M
- 寻址的时间为传输的时间的1%时,是最佳状态。
二、HDFS的Shell操作
2.1 基本语法
hadoop fs 具体命令
hdfs dfs 具体命令
2.2 命令大全
[hadoop@master ~]$ hadoop fs
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] <localsrc> ... <dst>]
[-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-count [-q] [-h] <path> ...]
[-cp [-f] [-p | -p[topax]] <src> ... <dst>]
[-createSnapshot <snapshotDir> [<snapshotName>]]
[-deleteSnapshot <snapshotDir> <snapshotName>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] <path> ...]
[-expunge]
[-find <path> ... <expression> ...]
[-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-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] [-l] <localsrc> ... <dst>]
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] <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] <file>]
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touchz <path> ...]
[-truncate [-w] <length> <path> ...]
[-usage [cmd ...]]
Generic options supported are
-conf <configuration file> specify an application configuration file
-D <property=value> use value for given property
-fs <local|namenode:port> specify a namenode
-jt <local|resourcemanager:port> specify a ResourceManager
-files <comma separated list of files> specify comma separated files to be copied to the map reduce cluster
-libjars <comma separated list of jars> specify comma separated jar files to include in the classpath.
-archives <comma separated list of archives> specify comma separated archives to be unarchived on the compute machines.
The general command line syntax is
bin/hadoop command [genericOptions] [commandOptions]
2.3 常用命令
- 启动Hadoop集群
[hadoop@master ~]$ start-dfs.sh
Starting namenodes on [master]
master: starting namenode, logging to /opt/software/hadoop-2.7.2/logs/hadoop-hadoop-namenode-master.out
slave02: starting datanode, logging to /opt/software/hadoop-2.7.2/logs/hadoop-hadoop-datanode-slave02.out
slave01: starting datanode, logging to /opt/software/hadoop-2.7.2/logs/hadoop-hadoop-datanode-slave01.out
slave03: starting datanode, logging to /opt/software/hadoop-2.7.2/logs/hadoop-hadoop-datanode-slave03.out
Starting secondary namenodes [slave01]
slave01: starting secondarynamenode, logging to /opt/software/hadoop-2.7.2/logs/hadoop-hadoop-secondarynamenode-slave01.out
[hadoop@master ~]$ start-yarn.sh
starting yarn daemons
starting resourcemanager, logging to /opt/software/hadoop-2.7.2/logs/yarn-hadoop-resourcemanager-master.out
slave01: starting nodemanager, logging to /opt/software/hadoop-2.7.2/logs/yarn-hadoop-nodemanager-slave01.out
slave02: starting nodemanager, logging to /opt/software/hadoop-2.7.2/logs/yarn-hadoop-nodemanager-slave02.out
slave03: starting nodemanager, logging to /opt/software/hadoop-2.7.2/logs/yarn-hadoop-nodemanager-slave03.out
- -help:输出这个命令参数
[hadoop@master ~]$ hadoop fs -help rm
-rm [-f] [-r|-R] [-skipTrash] <src> ... :
Delete all files that match the specified file pattern. Equivalent to the Unix
command "rm <src>"
-skipTrash option bypasses trash, if enabled, and immediately deletes <src>
-f If the file does not exist, do not display a diagnostic message or
modify the exit status to reflect an error.
-[rR] Recursively deletes directories
- -ls:显示目录信息
[hadoop@master ~]$ hadoop fs -ls /
Found 8 items
-rw-r--r-- 3 hadoop supergroup 289927651 2020-11-23 11:14 /Yuri_s_Revenge_1.001.exe
drwxr-xr-x - hadoop supergroup 0 2020-11-25 13:40 /flowbean
drwxr-xr-x - hadoop supergroup 0 2020-11-30 15:52 /input
-rw-r--r-- 3 hadoop supergroup 603940 2020-11-23 10:43 /rename.pdf
drwxr-xr-x - hadoop supergroup 0 2020-11-24 21:03 /test.har
drwxrwx--- - hadoop supergroup 0 2020-11-22 21:31 /tmp
drwxrwxrwx - root root 0 2020-11-25 12:45 /wordcount
-rw-r--r-- 3 hadoop supergroup 10485760 2020-11-23 12:25 /算法笔记.胡凡.pdf
- -mkdir:在HDFS上创建目录
[hadoop@master ~]$ hadoop fs -mkdir -p /sanguo/shuguo
- -moveFromLocal:从本地剪切,粘贴到HDFS
[hadoop@master ~]$ hadoop fs -moveFromLocal data.txt /sanguo/sanguo
- -appendToFile:追加一个文件到已经存在的文件末尾
[hadoop@master ~]$ hadoop fs -appendToFile liubei.txt /sanguo/shuguo/kongming.txt
- cat:显示文件内容
[hadoop@master ~]$ hadoop fs -cat /sanguo/sanguo
hello hi hello hi
hi hello i am
fine thanks you
hi fine tha
- -chgrp 、-chmod、-chown:Linux文件系统中的用法一样,修改文件所属权限
- -copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去
- -copyToLocal:从HDFS拷贝到本地
- -cp:从HDFS的一个路径拷贝到HDFS的另一个路径
- -mv:在HDFS目录中移动文件
- -get:等同于copyToLocal,就是从HDFS下载文件到本地
- -getmerge:合并下载多个文件,比如HDFS的目录 /aaa/下有多个文件:log.1, log.2,log.3,...
[hadoop@master ~]$ hadoop fs -getmerge /wordcount/input/hi* /opt/software/hi.txt
- -put:等同于copyFromLocal
- -tail:显示一个文件的末尾
- -rm:删除文件或文件夹
- -rmdir:删除空目录
- -du统计文件夹的大小信息
- -setrep:设置HDFS中文件的副本数量,这里的副本数量只是记录在NameNode的元数据中,是否真的会有这么多的副本,还得看DataNode的数量。
三、HDFS客户端操作
3.1 HDFS环境配置
- 根据自己电脑的操作系统拷贝对应的编译后的hadoop jar包到非中文路径
- 配置HADOOP_HOME环境变量
- 配置PATH环境变量
- 创建一个Maven工程
- 导入相应的依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
</dependencies>
3.2 HDFS的API操作
3.2.1 HDFS的API基本操作
package cqupt.tiger.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
/*
* 1、FileSystem:文件系统的抽象基类
* FileSystem的实现取决于fs.defaultFS的配置
* 有两种实现!
* LocalFileSystem:本地文件系统
* DistributedFileSystem:分布式文件系统
*
* 声明用户身份
*
* 2、Configuration:功能是读取配置文件中的参数
* Configuration在读取配置文件参数时,根据文件名,从类路径按照顺序读取配置文件
* 先读取xxx-default.xml,再读取xxx-site.xml
* Configuration类一加载,就会默认读取8个配置文件
* 将8个配置文件中所有属性,读取到一个Map集合中
* 同时提供了set方法,来手动设置用户自定义的参数
*
* 3、FileStatus
* 代表一个文件的状态(文件的属性信息)
*
* 4、offset和length
* offset:是偏移量:指块在我呢就爱你中的起始位置
* length:是块的大小
*
* 5、LocatedFileStatus
* LocatedFileStatus是FileStatus的子类,除了文件的属性,还有块的位置信息!
*
* */
public class TestHDFS {
private FileSystem fs = null;
private Configuration conf = null;
@Before
public void init() throws Exception {
conf = new Configuration();
// 创建一个客户端对象
// FileSystem fs = FileSystem.get(conf);
fs = FileSystem.get(new URI("hdfs://master:9000"), conf, "hadoop");
}
/*
* hadoop fs(运行一个通用的用户客户端) -mkdir /xxx
* 创建一个客户端对象,调用创建目录的方法,路径作为方法的参数传入
* */
@Test
public void testMkdir() throws Exception {
fs.mkdirs(new Path("/idea"));
}
// 上传文件
@Test
public void uploadFile() throws IOException {
fs.copyFromLocalFile(false, true, new Path("/Users/shiwenhu/Downloads/Yuri_s_Revenge_1.001.exe"), new Path("/"));
}
// 下载文件
@Test
public void testDownload() throws IOException {
fs.copyToLocalFile(false, new Path("/中国计算机学会推荐国际学术会议和期刊目录-2019.pdf"), new Path("/Users/shiwenhu/Desktop"), false);
}
// 删除文件:hadoop fs -rm -r -f 路径
@Test
public void testDelete() throws IOException {
fs.delete(new Path("/idea"), true);
}
// 重命名: hadoop fs -mv 源文件 目标文件
@Test
public void testRename() throws IOException {
fs.rename(new Path("/中国计算机学会推荐国际学术会议和期刊目录-2019.pdf"), new Path("/rename.pdf"));
}
// 判断当前路径是否存在
@Test
public void testIfPathExists() throws IOException {
System.out.println(fs.exists(new Path("/idea")));
}
// 判断当前路径是目录还是文件
@Test
public void testFileIsDir() throws IOException {
Path path = new Path("/rename.pdf");
// 不建议使用此方法,建议使用FileStatus
System.out.println(fs.isDirectory(path));
System.out.println(fs.isFile(path));
// 建议使用此方法
FileStatus fileStatus = fs.getFileStatus(path);
System.out.println("是否是目录:" + fileStatus.isDirectory());
System.out.println("是否是文件:" + fileStatus.isFile());
}
// 返回当前目录及其子目录里面所有内容
@Test
public void testDirList() throws IOException {
Path path = new Path("/wordcount");
FileStatus[] fileStatuses = fs.listStatus(path);
for (FileStatus fileStatus : fileStatuses) {
// 获取文件名path是完整路径,包含协议+文件名
Path fileStatusPath = fileStatus.getPath();
System.out.println(fileStatusPath + "是否是目录:" + fileStatus.isDirectory());
System.out.println(fileStatusPath + "是否是文件:" + fileStatus.isFile());
}
}
// 获取文件的块信息
@Test
public void testGetBlockInformation() throws IOException {
Path path = new Path("/Yuri_s_Revenge_1.001.exe");
RemoteIterator<LocatedFileStatus> status = fs.listLocatedStatus(path);
while(status.hasNext()) {
LocatedFileStatus next = status.next();
// 块的信息
BlockLocation[] blockLocations = next.getBlockLocations();
for (BlockLocation blockLocation : blockLocations) {
System.out.println(blockLocation);
System.out.println("----------------------------");
}
}
}
@After
public void close() throws IOException {
if (fs != null) {
fs.close();
}
}
}
3.2.2 HDFS 的I/O流操作
package cqupt.tiger.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
/*
* 1、上传文件时,只上传这个文件的一部分
*
* 2、下载文件时,如何只下载文件的某一个块,或者只下载文件的一部分
*
* */
public class TestClientUploadAndDownload {
private FileSystem fs = null;
private FileSystem localFs = null;
private Configuration conf = null;
@Before
public void init() throws Exception {
conf = new Configuration();
// 创建一个客户端对象
// FileSystem fs = FileSystem.get(conf);
fs = FileSystem.get(new URI("hdfs://master:9000"), conf, "hadoop");
// 本地文件系统
localFs = FileSystem.get(new Configuration());
}
// 只上传文件的前10M
/*
* 官方实现:
* contents = null;
FSDataOutputStream out = null;
try {
InputStream in = srcFS.open(src);
out = dstFS.create(dst, overwrite);
IOUtils.copyBytes(in, out, conf, true);
} catch (IOException var11) {
IOUtils.closeStream(out);
IOUtils.closeStream(contents);
throw var11;
}
*
* */
@Test
public void testClientUpload() throws Exception {
// 提供两个Path,和两个FileSystem
Path src = new Path("/Users/shiwenhu/Downloads/算法笔记.胡凡.pdf");
Path dest = new Path("/算法笔记.胡凡.pdf");
// 使用本地文件系统中的输入流来读取本地文件
FSDataInputStream in = localFs.open(src);
// 使用HDFS的分布式文件系统获取的输出流,向dest路径写入数据
FSDataOutputStream out = fs.create(dest, true);
// 1K
// 流中数据拷贝
byte[] buffer = new byte[1024];
for (int i = 0; i < 1024 * 10; i++) {
in.read(buffer);
out.write(buffer);
}
// 关闭
IOUtils.closeStream(in);
IOUtils.closeStream(out);
}
// 自定义下载
@Test
public void testFirstBlock() throws IOException {
// 提供两个Path和两个FileSystem
Path src = new Path("/算法笔记.胡凡.pdf");
Path dest = new Path("/Users/shiwenhu/Desktop/firstblock");
// 使用HDFS的分布式文件系统获取输入流,读取HDFS上指定路径的数据
FSDataInputStream in = fs.open(src);
// 使用本地文件系统中获取的输出流写入本地文件
FSDataOutputStream out = localFs.create(dest, true);
//
byte[] buffer = new byte[1024];
for (int i = 0; i < 1024 * 128; i++) {
in.read(buffer);
out.write(buffer);
}
// 关闭
IOUtils.closeStream(in);
IOUtils.closeStream(out);
}
@Test
public void testSecondBlock() throws IOException {
// 提供两个Path和两个FileSystem
Path src = new Path("/算法笔记.胡凡.pdf");
Path dest = new Path("/Users/shiwenhu/Desktop/secondblock");
// 使用HDFS的分布式文件系统获取输入流,读取HDFS上指定路径的数据
FSDataInputStream in = fs.open(src);
// 使用本地文件系统中获取的输出流写入本地文件
FSDataOutputStream out = localFs.create(dest, true);
// 定位到流的指定位置
in.seek(1024 * 1024 * 128);
//
byte[] buffer = new byte[1024];
for (int i = 0; i < 1024 * 128; i++) {
in.read(buffer);
out.write(buffer);
}
// 关闭
IOUtils.closeStream(in);
IOUtils.closeStream(out);
}
@After
public void close() throws IOException {
if (fs != null) {
fs.close();
}
}
}
四、HDFS的数据流
4.1 HDFS写数据流程
4.1.1 文件写入
- HDFS写数据流程,如下图:
- 客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标是否已经存在,父目录是否已存在
- NameNode返回是否可以上传
- 客户端请求上传第一个Block上传到哪几个DataNode服务器上
- NameNode返回3个DataNode节点,分别为dn1,dn2,dn3
- 客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成
- dn1,dn2,dn3逐级应答客户端
- 客户端开始网dn1上传到第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答
- 当一个Block传输完成之后,客户端再次请求NameNode长串第二个Block的服务器
4.1.2 机架感知(副本存储节点选择)
- 第一个副本在Client所处的节点上,如果客户端在集群外,随机选择一个
- 第二个副本和第一个副本位于相同机架,随机节点
- 第三个副本位于不同机架,随机节点
4.2 HDFS读数据流程
4.2.1 HDFS读数据流程
- HDFS读数据流程如下图所示:
- 客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址
- 挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据
- DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)
- 客户端以Packet为单位接收,现在本地缓存,然后写入目标文件
五、NameNode和SecondaryNameNode
5.1 NameNode和SecondNameNode工作机制
5.1.1 工作流程
- 第一阶段:NameNode启动
- 第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存
- 客户端对元数据进行增删改的请求
- NameNode记录操作日志,更新滚动日志
- nameNode在内存中对数据进行增删改
- 第二阶段
- Secondary NameNode询问NameNode是否需要CheckPoint,直接带回NameNode是否检查结果
- Secondary NameNode请求执行CheckPoint
- 将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode
- Secondary NameNode 加载编辑日志和镜像文件到内存,并合并
- 生成新的镜像文件fsimage.chkpoint
- 拷贝fsimage.chkpoint到NameNode
- NameNode将fsimage.chkpoint重命名为fsimage
5.1.2 NameNode和Secondary NameNode工作机制详解
- fsimage:NameNode内存中元数据序列化后形成的文件
- Edits:记录客户端更新元数据信息的每一步操作(可通过Edits运算出元数据)。
- NameNode启动时,先滚动Edits并生成一个空的edits.inprogress,然后加载Edits和Fsimage到内存中,此时NameNode内存就持有最新的元数据信息。Client开始对NameNode发送到元数据的增删改的请求,这些请求的操作首先会被记录到edits.inprogress中(查询元数据的操作不会被记录在Edits中,因为查询操作不会更改元数据信息),如果此时NameNode挂掉,重启后会从Edits中读取信息。然后,NameNode会在内存中执行元数据的增删改操作。
- 由于Edits中记录的操作会越来越多,Edits文件就会越来越大,导致nameNode在启动加载Edits时会很慢,所以需要对Edits和Fsimage进行合并(所谓合并,就是将Edits和Fsimage加载到内存中,照着Edits中的操作一步步执行,最终形成新的Fsimage)
- SecondaryNameNode的作用就是帮助NameNode进行Edits和Fsimage的合并工作
- SecondaryNameNode首先会询问NameNode是否需要CheckPoint操作(触发CheckPoint需要满足两个条件中的任意一个,定时时间到和Edits中数据写满了)。直接带回nameNode是否检查结果。
- SecondaryNameNode执行CheckPoint操作,首先会让NameNode滚动并生成一个空的Edits.inprogress,滚动Edits的目的是给Edits打个标记,以后所有新的操作都写入edits.inprogress,其他未合并的Edits和Fsimage会拷贝到SecondaryNameNode的本地,然后将拷贝的Edits和Fsimage加载到内存中进行合并,生成fsimage.chkpoint,然后将fsimage.chkpoint拷贝给NameNode,重命名为Fsimage后替换掉原来的Fsimage。
- NameNode在启动时就只需要加载之前未合并的Edits和Fsimage即可,因为合并过的Edits中的元数据信息已经被记录在Fsimage中。
5.2 Fsimage和Edits解析
- Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件idnode的序列化信息。
- Edits文件:存放在HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到Edits文件中
- seen_txid文件保存的是一个数字,就是最后一个edits_的数字
- 每次nameNode启动的时候都会将Fsimage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的,同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合并
5.3 CheckPoint时间设置
- 通常情况下,SecondaryNameNode每隔一个小时执行一次,或者当操作次数达到1百万此时候执行,也可以通过配置hdfs-site.xml来配置
<property>
<name>dfs.namenode.checkpoint.txns</name>
<value>1000000</value>
<description>操作动作次数</description>
</property>
<property>
<name>dfs.namenode.checkpoint.check.period</name>
<value>60</value>
<description> 1分钟检查一次操作次数</description>
</property >
5.4 NameNode故障处理
- NameNode故障后,可以采用如下两种方法恢复数据
5.4.1 方法一
- 将SecondaryNameNode中数据拷贝到NameNode存储数据的目录
- kill掉NameNode进程
- 删除NameNode存储的数据(/data/tmp/dfs/name)
- 拷贝SecondaryNameNode中数据到原NameNode存储数据目录
- 重新启动NameNode
5.4.2 方法二
- 使用-importCheckpoint选项启动NameNode守护进程,从而将SecondaryNameNode中数据拷贝到NameNode目录中
- 修改hdfs-site.xml中
property>
<name>dfs.namenode.checkpoint.period</name>
<value>120</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/opt/software/hadoop-2.7.2/data/tmp/dfs/name</value>
</property>
- kill掉NameNode进程
- 删除NameNode存储的数据(/data/tmp/dfs/name)
- 如果SecondaryNameNode不和NameNode在一个主机节点上,需要将SecondaryNameNode存储数据的目录拷贝到NameNode存储数据的平级目录,并删除in_use.lock文件
- 导入检查点数据(等一会ctrl+c结束)
- 启动NameNode
5.5 集群安全模式
5.5.1 概述
- NameNode启动:首先将镜像文件(Fsimage)载入内存,并执行编辑日志(Edits)中的各项操作,一旦在内存中成功建立文件系统元数据的映像,则创建一个新的Fsimage文件和一个空的编辑日志,此时,NameNode开始监听DataNode请求。这个过程期间,nameNode一直运行在安全模式,即NameNode的文件系统对于客户端来说是只读的
- DataNode启动:系统中的数据块的位置并不是由NameNode维护的,而是以块列表的形式存储在DataNode中,在系统正常操作期间,NameNode会在内存中保留所有块位置的映射信息,在安全模式下,各个DataNode会向NameNode发动最新的块信息列表信息,NameNode了解到足够多的块位置信息后,即可高效运行文件系统
- 安全模式退出判断:如果满足“最小副本条件”,NameNode会在30秒钟之后就退出安全模式,所谓的最小副本条件指的是在整个文件系统中99.9%的块满足最小副本级别(默认值:dfs.replication.min=1)。在启动一个刚刚格式化的HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式
5.5.2 基本语法
- hdfs dfsadmin -safemode get:查看安全模式状态
- hdfs dfsadmin -safemode enter:进入安全模式状态
- hdfs dfsadmin -safemode leave:离开安全模式状态
- hdfs dfsadmin -safemode wait:等待安全模式状态
5.6 NameNode多目录配置
- NameNode的本地目录可以配置成多个,且每个目录存放内容相同,增加了可靠性,具体配置如下:
- 在hdfs-site.xml文件中增加如下内容
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///${hadoop.tmp.dir}目录1,file:///${hadoop.tmp.dir}目录2</value>
</property>
- 停止集群,删除data和logs中的所有数据
- 格式化集群并启动
六、DataNode
6.1 DataNode工作机制
- 一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳
- DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息
- 心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用
- 集群运行中可以安全加入和退出一些机器
6.2 数据的完整性
- 当DataNode读取Block的时候,会计算CheckSum
- 如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏
- Client读取其他DataNode上的Block
- DataNode在其文件创建后周期验证CheckSum
6.3 掉线参数设置
- DataNode进程死亡或者网络故障导致DataNode无法与NameNode通信
- NameNode不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称为超时时长
- HDFS默认的超时时长为10min+30s
- 如果定义超时时间为TimeOut,则超时时长计算公式如下:
TimeOut = 2 * dfs.namenode.heartbeat.recheck-interval + 10 * dfs.heartbeat.interval
# 默认的dfs.namenode.heartbeat.recheck-interval大小为5min,dfs.heartbeat.interval默认为3s
- 需要注意的是hdfs-site.xml配置文件中的dfs.heartbeat.interval的单位为毫秒,dfs.namenode.heartbeat.recheck-interval的单位为秒
<property>
<name>dfs.namenode.heartbeat.recheck-interval</name>
<value>300000</value>
</property>
<property>
<name> dfs.heartbeat.interval </name>
<value>3</value>
</property>
6.4 服役新数据节点
- 直接启动DataNode,即可关联到集群
[hadoop@master software]$ hadoop-daemon.sh start datanode
[hadoop@master software]$ yarn-daemon.sh start nodemanager
- 如果数据不平衡,可以使用命令实现集群再平衡
[hadoop@master software]$ start-balancer.sh
6.5 退役旧数据节点
6.5.1 添加白名单
- 添加到白名单的主机,都允许访问NameNode,不在白名单的主机,都会被退出,具体步骤如下;
- 在NameNode的/opt/software/hadoop-2.7.2/etc/hadoop目录下面创建dfs.hosts文件,在文件中添加相应的主机名
- 在NameNode的hdfs-site.xml配置文件中增加dfs.hosts属性
<property>
<name>dfs.hosts</name>
<value>/opt/software/hadoop-2.7.2/etc/hadoop/dfs.hosts</value>
</property>
- 将修改后的hdfs-site.xml文件分发给每一个节点
- 刷新NameNode
[hadoop@master software]$ hdfs dfsadmin -refreshNodes
- 更新ResourceManager节点
[hadoop@master software]$ yarn rmadmin -refreshNodes
- 如果数据不均衡,可以用命令实现集群的再平衡
[hadoop@master software]$ start-balancer.sh
6.5.2 黑名单退役
- 在黑名单上面的主机都会被强制退出
- 在NameNode的/opt/software/hadoop-2.7.2/etc/hadoop目录下创建dfs.hosts.exclude文件
- 添加要退役的节点
- 在NameNode的hdfs-site.xml配置文件中增加dfs.hosts.exclude属性
<property>
<name>dfs.hosts.exclude</name>
<value>/opt/software/hadoop-2.7.2/etc/hadoop/dfs.hosts.exclude</value>
</property>
- 刷新NameNode、刷新ResourceManager
[hadoop@master software]$ hdfs dfsadmin -refreshNodes
[hadoop@master software]$ yarn rmadmin -refreshNodes
- 检查Web浏览器,退役节点的状态为decommission in progress(退役中),说明数据节点正在复制块到其他节点。
- 等待退役节点状态为decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。注意:如果副本数是3,服役的节点小于等于3,是不能退役成功的,需要修改副本数后才能退役。
- 如果数据不均衡,可以用命令实现集群的再平衡
[hadoop@master software]$ start-balancer.sh
注意:不允许白名单和黑名单中同时出现一个主机名称。
6.6 DataNode多目录配置
- DataNode也可以配置成多个目录,每个目录存储的数据不一样。即:数据不是副本。
- 修改hdfs-site.xml配置
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///${hadoop.tmp.dir}目录1,file:///${hadoop.tmp.dir}目录2</value>
</property>
七、HDFS 2.x新特性
7.1 集群间数据拷贝
- scp实现两个远程主机之间的文件复制
- 采用distcp命令实现两个Hadoop集群之间的递归数据复制
hadoop distcp 待拷贝URI 目标URI
7.2 Hadoop存档
- HDFS存储小文件的弊端
- 每个文件均按块存储,每个块的元数据在NameNode的内存中,因此Hadoop存储文件会非常低效。因为大量小文件会耗尽NameNode中的大部分内存。但注意,存储小文件所需要的磁盘容量和数据块大小无关。
- 解决存储小文件办法之一
- Hadoop存档文件或HAR文件,是一个更高效的文件存档工具,它将文件存入HDFS块,在减少NameNode内存使用的同时,允许对文件进行透明的访问。具体来说Hadoop存档文件对内还是一个一个独立文件,对NameNode而言却是一个整体,减少了NameNode的内存消耗
- 案例:把/input目录里面的所有文件归档称为一个input.har归档文件,并把归档后文件存储到/output路径下
[hadoop@master software]$ hadoop archive -archiveName input.har –p /input /output
- 查看归档
[hadoop@master software]$ hadoop fs -lsr /output/input.har
[hadoop@master software]$ hadoop fs -lsr har:///output/input.har
- 解归档文件
[hadoop@master software]$ hadoop fs -cp har:///output/input.har/* /user/test