Hadoop学习笔记–持续更新
入门
代码语言:javascript复制?⭐️???? 我是高亮
<span style='color:文字颜色;background:背景颜色;font-size:文字大小;font-family:字体;'>文字</span>
⭐️1、大数据部门组织结构
⭐️2、HDFS架构
代码语言:javascript复制1、NameNode(nn):它是一个主管、管理者
①.存储文件的元数据,如文件名、文件目录结构、文件属性(生成时间、副本数、文件的权限等等),以及每个文件的块列表和块所在的DataNode等。
②.配置副本策略。
③.管理HDFS的名称空间、管理数据块(Block)映射信息。
④.处理客户端读写请求。
2、DataNode(dn):NameNode下达命令,DataNode执行实际的操作。
①.存储实际的数据块。
②.执行数据块的读/写操作。
3、Secondary NameNode(2nn):并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。
①.辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode。
②.在紧急情况下,可辅助恢复NameNode。
4、Client:客户端
①.文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传。
②.与NameNode交互,获取文件的位置信息。
③.与DataNode交互,读取或者写入数据。
④.Client提供一些命令来管理HDFS,比如NameNode格式化。
⑤.Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作。
⭐️3、Yarn架构
代码语言:javascript复制1、ResourceManager(RM):
①.处理客户端请求
②.监控NodeManager
③.启动或监控ApplicationMaster
④.资源的分配与调度
2、NodeManager(NM)
①.管理单个节点上的资源
②.处理来自ResourceManager的命令
③.处理来自ApplicationMaster的命令
3、ApplicationMaster(AM)
①.负责数据的切分
②.为应用程序申请资源并分配给内部的任务
③.任务的监控与容错
4、Container
①.Container是YARN中的资源抽象,它封装了某个节点上的多维度资源,如内存、CPU、磁盘、网络等
⭐️4、Hadoop集群配置
配置规划
master | slave1 | slave2 | |
---|---|---|---|
HDFS | NameNode、DataNode | DataNode | SecondaryNameNode、DataNode |
YARN | NodeManager | ResourceManager、NodeManager | NodeManager |
1、HDFS
代码语言:javascript复制?hadoop-env.sh
~]# echo $JAVA_HOME
/usr/java/jdk1.8.0_144
## 修改JAVA_HOME 路径
export JAVA_HOME=/usr/java/jdk1.8.0_144
代码语言:javascript复制?core-site.xml
<!-- 指定HDFS中NameNode的地址 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://master:9000</value>
</property>
<!-- 指定Hadoop运行时产生文件的存储目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/hadoop/data/tmp</value>
</property>
代码语言:javascript复制?hdfs-site.xml
<!-- 指定HDFS副本的数量 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 指定Hadoop辅助节点(SecondaryNameNode)主机配置 -->
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>slave2:50090</value>
</property>
2、Yarn
代码语言:javascript复制?yarn-env.sh
## 配置一下JAVA_HOME
export JAVA_HOME=/usr/java/jdk1.8.0_144
代码语言:javascript复制?yarn-site.xml
<!-- Reducer获取数据的方式 -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<!-- 指定YARN的ResourceManager的地址 -->
<property>
<name>yarn.resourcemanager.hostname</name>
<value>slave1</value>
</property>
代码语言:javascript复制?mapred-env.sh
## 配置一下JAVA_HOME
export JAVA_HOME=/usr/java/jdk1.8.0_144
代码语言:javascript复制?mapred-site.xml (对mapred-site.xml.template重新命名为) mv mapred-site.xml.template mapred-site.xml
<!-- 指定MR运行在YARN上 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
3、配置slave(群起)
代码语言:javascript复制?slaves 路径:$HADOOP_HOME/etc/hadoop/slaves ==注意:==该文件中添加的内容结尾不允许有空格,文件中不允许有空行。
## vim slaves
master
slave1
slave2
4、配置历史服务器
代码语言:javascript复制?mapred-site.xml
<!-- 历史服务器端地址 -->
<property>
<name>mapreduce.jobhistory.address</name>
<value>master:10020</value>
</property>
<!-- 历史服务器web端地址 -->
<property>
<name>mapreduce.jobhistory.webapp.address</name>
<value>master:19888</value>
</property>
代码语言:javascript复制## 启动历史服务器
mr-jobhistory-daemon.sh start historyserver
## 查看历史服务器是否启动
jps
## 查看JobHistory
http://master:19888/jobhistory
5、配置日志的聚集
代码语言:javascript复制==概念:==应用运行完成以后,将程序运行日志信息上传到HDFS系统上。 ==好处:==可以方便的查看到程序运行详情,方便开发调试。 ==注意:==开启日志聚集功能,需要重新启动NodeManager 、ResourceManager和HistoryManager。
?yarn-site.xml
<!-- 日志聚集功能使能 -->
<property>
<name>yarn.log-aggregation-enable</name>
<value>true</value>
</property>
<!-- 日志保留时间设置7天 -->
<property>
<name>yarn.log-aggregation.retain-seconds</name>
<value>604800</value>
</property>
6、Hadoop分发到各个节点
代码语言:javascript复制## xsync脚本详情见:7、脚本
xsync /usr/hadoop/
7、SHH免密登入
代码语言:javascript复制?免密登入原理
?具体操作(root用户)
## 1.生成公钥和私钥:
ssh-keygen -t rsa
然后敲(三个回车),就会生成两个文件id_rsa(私钥)、id_rsa.pub(公钥)
## 2.将公钥拷贝到要免密登录的目标机器上(包括自己)
ssh-copy-id master
ssh-copy-id slave1
ssh-copy-id slave2
## 注意:此时master可以免密登入master、slave1、slave2,但slave1、slave2不能反向登入master,还需要去slave1、slave2去重复执行上述操作
?.ssh文件夹下(~/.ssh)的文件功能解释
known_hosts | 记录ssh访问过计算机的公钥(public key) |
---|---|
id_rsa | 生成的私钥 |
id_rsa.pub | 生成的公钥 |
authorized_keys | 存放授权过得无密登录服务器公钥 |
⭐️5、常用命令
1、HDFS
代码语言:javascript复制?格式化NameNode(第一次启动时格式化,以后就不要总格式化)
hdfs namenode -format
代码语言:javascript复制?单节点单服务启动NameNode、DataNode
hadoop-daemon.sh start namenode
hadoop-daemon.sh start datanode
代码语言:javascript复制?启动HDFS集群
start-dfs.sh
代码语言:javascript复制?操作集群命令
## 创建一个input文件夹
hdfs dfs -mkdir -p /user/input
## 上传文件
hdfs dfs -put /usr/hadoop/input/wordcount.txt /user/input/wc.input
## 查看目录下内容
hdfs dfs -ls /user/input/
## 查看文件内容
hdfs dfs -cat /user/input/wc.input
## 下载文件到本地
hdfs dfs -get /user/input/wc.input ./input/
## 删除文件
hdfs dfs -rm (-r) /user/input/wc.input
## 追加文件到已存在文件的末尾
hdfs dfs -appendToFile liubei.txt(新文件) /sanguo/shuguo/kongming.txt(HDFS上已存在的文件)
## 设置HDFS中文件的副本数量
hdfs dfs -setrep 10 /sanguo/shuguo/kongming.txt
#注意:
这里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10。
2、Yarn
代码语言:javascript复制?单节点启动
①.启动前必须保证NameNode和DataNode已经启动
②.启动ResourceManager yarn-daemon.sh start resourcemanager
③.启动NodeManager yarn-daemon.sh start nodemanager
代码语言:javascript复制?群起集群 ==注意:==在装有ResourceManager服务的节点上启动,即在slave1上启动
start-yarn.sh
代码语言:javascript复制?Yarn的WebUI界面
http://slave1:8088
3、集群启动、停止方法总结
代码语言:javascript复制?各个模块分开启动/停止(配置ssh是前提)常用
## 整体启动/停止HDFS
start-dfs.sh / stop-dfs.sh
## 整体启动/停止YARN (在有ResourceManager服务的节点上)
start-yarn.sh / stop-yarn.sh
代码语言:javascript复制?各个服务组件逐一启动/停止
## 分别启动/停止HDFS组件
hadoop-daemon.sh start / stop namenode / datanode / secondarynamenode
## 启动/停止YARN
yarn-daemon.sh start / stop resourcemanager / nodemanager
⭐️6、时间同步
⏰crontab 定时任务设置 ==语法:==crontab -e 编辑crontab定时任务 crontab -l 查询crontab任务 crontab -r 删除当前用户所有的crontab任务
?* * * * * 每个*的含义
含义 | 范围 | |
---|---|---|
第一个"*" | 一小时当中的第几分钟 | 0-59 |
第二个"*" | 一天当中的第几小时 | 0-23 |
第三个"*" | 一个月当中的第几天 | 1-31 |
第四个"*" | 一年当中的第几月 | 1-12 |
第五个"*" | 一周当中的星期几 | 0-7(0和7都代表星期日) |
特殊符号 | 含义 |
---|---|
* | 代表任何时间。比如第一个“*”就代表一小时中每分钟都执行一次的意思。 |
, | 代表不连续的时间。比如“0 8,12,16 * * * 命令”,就代表在每天的8点0分,12点0分,16点0分都执行一次命令 |
- | 代表连续的时间范围。比如“0 5 * * 1-6命令”,代表在周一到周六的凌晨5点0分执行命令 |
*/n | 代表每隔多久执行一次。比如“*/10 * * * * 命令”,代表每隔10分钟就执行一遍命令 |
?案例
时间 | 含义 |
---|---|
45 22 * * * 命令 | 每天在22点45分执行命令 |
0 17 * * 1 命令 | 每周1 的17点0分执行命令 |
0 5 1,15 * * 命令 | 每月1号和15号的凌晨5点0分执行命令 |
40 4 * * 1-5 命令 | 每周一到周五的凌晨4点40分执行命令 |
*/10 4 * * * 命令 | 每天的凌晨4点,每隔10分钟执行一次命令 |
0 0 1,15 * 1 命令 | 每月1号和15号,每周1的0点0分都会执行命令。注意:星期几和几号最好不要同时出现 |
?时间同步原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-22uhcfxh-1617677655640)(D:Code_Study博客笔记Hadoop学习笔记.assets1604300154815.png)]
代码语言:javascript复制?具体操作(root用户)
## 检查ntp是否安装
rpm -qa|grep ntp
## 修改/etc/ntp.conf配置文件
## 修改1 (授权192.168.1.0-192.168.1.255网段上的所有机器可以从这台机器上查询和同步时间)
## 去掉注释,修改IP段
restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
## 修改2 (集群在局域网中,不使用其他互联网上的时间)
## 添加注释
server 0.centos.pool.ntp.org iburst
server 1.centos.pool.ntp.org iburst
server 2.centos.pool.ntp.org iburst
server 3.centos.pool.ntp.org iburst
## 添加3 (当该节点丢失网络连接,依然可以采用本地时间作为时间服务器为集群中的其他节点提供时间同步)
server 127.127.1.0
fudge 127.127.1.0 stratum 10
## 修改/etc/sysconfig/ntpd 文件
## 让硬件时间与系统时间一起同步
SYNC_HWCLOCK=yes
## 重新启动ntpd服务
service ntpd status
## 设置ntpd服务开机启动
chkconfig ntpd on
?让其他节点十分钟同步一次 crontab -e */10 * * * * /usr/sbin/ntpdate master
⭐️7、常见问题
代码语言:javascript复制?思考:为什么不能一直格式化NameNode,格式化NameNode时要注意什么?
格式化NameNode,会产生新的集群id,导致NameNode和DataNode的集群id不一致,集群找不到已往数据。
所以,格式NameNode时,一定要先删除data数据和log日志,然后再格式化NameNode。
## 此处的data数据位置是在core-site.xml中配置的
⭐️8、脚本
代码语言:javascript复制?rsync 远程同步工具 rsync主要用于备份和镜像。具有速度快、避免复制相同内容和支持符号链接的优点。 rsync和scp区别:用rsync做文件的复制要比scp的速度快,rsync只对差异文件做更新。scp是把所有文件都复制过去。
?依赖rsync实现自动拷贝分发 vim xsync 脚本可放到/usr/local/bin 或任意已配置环境变量的目录下
#!/bin/bash
#1 获取输入参数个数,如果没有参数,直接退出
pcount=$#
if((pcount==0)); then
echo no args;
exit;
fi
#2 获取文件名称
p1=$1
fname=`basename $p1`
echo fname=$fname
#3 获取上级目录到绝对路径
pdir=`cd -P $(dirname $p1); pwd`
echo pdir=$pdir
#4 获取当前用户名称
user=`whoami`
#5 循环
for((host=1; host<3; host )); do
echo ------------------- hadoop$host --------------
rsync -rvl $pdir/$fname $user@slave$host:$pdir
done
代码语言:javascript复制##修改脚本 xsync 具有执行权限
chmod 777 xsync
HDFS
⭐️1、HDFS概述
1、HDFS优缺点
代码语言:javascript复制?优点
1、高容错性
①.数据自动保存多个副本。它通过增加副本的形式,提高容错性。
②.某一个副本丢失以后,它可以自动恢复。
2、适合处理大数据
①.数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据。
②.文件规模:能够处理百万规模以上的文件数量,数量相当之大。
3、可构建在廉价机器上,通过多副本机制,提高可靠性。
代码语言:javascript复制?缺点
1、不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
2、无法高效的对大量小文件进行存储
①.存储大量小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的。
②.小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
3、不支持并发写入、文件随机修改
①.一个文件只能有一个写,不允许多个线程同时写。
②.仅支持数据append(追加),不支持文件的随机修改。
2、HDFS块大小
代码语言:javascript复制?块大小计算、决定因素
## HDFS中的文件在物理上是分块存储(Block),块的大小可以通过配置参数( dfs.blocksize)来规定,默认大小在Hadoop2.x版本中是128M,老版本中是64M。
1、客户端查找集群中的block
2、假设寻址时间约为10ms,即查找到目标block的时间为10ms。
3、寻址时间为传输时间的1%时,则为最佳状态。因此,传输时间=10ms/0.01=1000ms=1s
4、而目前磁盘的传输速率普遍为100MB/s。
5、计算block大小=1s*100MB/s=100MB。
代码语言:javascript复制?==思考:==为什么块的大小不能设置太小,也不能设置太大?
1、HDFS的块设置太小,会增加寻址时间,程序一直在找块的开始位置。
2、如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢。
?==总结:==HDFS块的大小设置主要取决于磁盘传输速率。 如果公司采用固态硬盘则块大小可设置为256M
⭐️2、HDFS的客户端操作
1、环境准备
代码语言:javascript复制?Windows环境准备
1、解压Windows环境的Hadoop包
2、配置环境变量
3、创建一个Maven工程
4、导入相对应的依赖
5、配置log4j
pom文件
代码语言:javascript复制<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>
log4j配置
代码语言:javascript复制## 需要在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入
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
2、API操作
代码语言:javascript复制⭐️注意: 1、可能会报错用户(Administrator)权限问题 在Configurations中的VM arguments中添加配置-DHADOOP_USER_NAME=root, 该root为操作HDFS的用户名。 2、配置参数的优先级 参数优先级排序:(1).客户端代码中设置的值 >(2).ClassPath下的用户自定义配置文件 >(3).然后是服务器的默认配置
?HDFS文件下载
@Test
public void testCopyToLocalFile() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");
// 2 执行下载操作
// boolean delSrc 指是否将原文件删除
// Path src 指要下载的文件路径
// Path dst 指将文件下载到的路径
// boolean useRawLocalFileSystem 是否开启文件校验
fs.copyToLocalFile(false, new Path("/banzhang.txt"), new Path("e:/banhua.txt"), true);
// 3 关闭资源
fs.close();
}
代码语言:javascript复制?HDFS文件夹删除
@Test
public void testDelete() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");
// 2 执行删除
fs.delete(new Path("/input/"), true);
// 3 关闭资源
fs.close();
}
代码语言:javascript复制?HDFS文件名更改
@Test
public void testRename() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");
// 2 修改文件名称
fs.rename(new Path("/test1.txt"), new Path("/test222.txt"));
// 3 关闭资源
fs.close();
}
代码语言:javascript复制?HDFS文件详情查看
@Test
public void testListFiles() throws IOException, InterruptedException, URISyntaxException{
// 1获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");
// 2 获取文件详情
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
while(listFiles.hasNext()){
LocatedFileStatus status = listFiles.next();
// 输出详情
// 文件名称
System.out.println(status.getPath().getName());
// 长度
System.out.println(status.getLen());
// 权限
System.out.println(status.getPermission());
// 分组
System.out.println(status.getGroup());
// 获取存储的块信息
BlockLocation[] blockLocations = status.getBlockLocations();
for (BlockLocation blockLocation : blockLocations) {
// 获取块存储的主机节点
String[] hosts = blockLocation.getHosts();
for (String host : hosts) {
System.out.println(host);
}
}
System.out.println("-----------分割线----------");
}
// 3 关闭资源
fs.close();
}
代码语言:javascript复制?HDFS文件和文件夹判断
@Test
public void testListStatus() throws IOException, InterruptedException, URISyntaxException{
// 1 获取文件配置信息
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://master:9000"), configuration, "root");
// 2 判断是文件还是文件夹
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());
}
}
// 3 关闭资源
fs.close();
}
⭐️3、HDFS的数据流(读写)
1、写流程
?写数据流程总结概括
代码语言:javascript复制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步)。
?网络拓扑-节点距离计算
? ==思考:==在HDFS写数据的过程中,NameNode会选择距离待上传数据最近距离的DataNode接收数据。那么这个最近距离怎么计算呢? ==节点距离:==两个节点到达最近的共同祖先的距离总和。
? 副本节点选择
代码语言:javascript复制第一个副本在Client所处的节点上。如果客户端在集群外,随机选一个。
第二个副本和第一个副本位于相同机架,随机节点。
第三个副本位于不同机架,随机节点。
2、读流程
?写数据流程总结概括
代码语言:javascript复制1、客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
2、挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
3、DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
4、客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
⭐️4、NN和2NN
1、NN和2NN工作机制
代码语言:javascript复制? 思考:NameNode中的元数据是存储在哪里的?
1、首先,我们做个假设,如果存储在NameNode节点的磁盘中,因为经常需要进行随机访问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数据的FsImage。
2、这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦NameNode节点断电,就会产生数据丢失。因此,引入Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到Edits中。这样,一旦NameNode节点断电,可以通过FsImage和Edits的合并,合成元数据。
3、但是,如果长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并。
代码语言:javascript复制? NN和2NN工作机制
1、第一阶段:NameNode启动
①.第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
②.客户端对元数据进行增删改的请求。
③.NameNode记录操作日志,更新滚动日志。
④.NameNode在内存中对数据进行增删改。
2、第二阶段:Secondary NameNode工作
①.Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
②.Secondary NameNode请求执行CheckPoint。
③.NameNode滚动正在写的Edits日志。
④.将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
⑤.Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
⑥.生成新的镜像文件fsimage.chkpoint。
⑦.拷贝fsimage.chkpoint到NameNode。
⑧.NameNode将fsimage.chkpoint重新命名成fsimage。
代码语言:javascript复制NN和2NN工作机制详解:
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并生成一个空的edits.inprogress,滚动Edits的目的是给Edits打个标记,以后所有新的操作都写入edits.inprogress,其他未合并的Edits和Fsimage会拷贝到SecondaryNameNode的本地,然后将拷贝的Edits和Fsimage加载到内存中进行合并,生成fsimage.chkpoint,然后将fsimage.chkpoint拷贝给NameNode,重命名为Fsimage后替换掉原来的Fsimage。NameNode在启动时就只需要加载之前未合并的Edits和Fsimage即可,因为合并过的Edits中的元数据信息已经被记录在Fsimage中。
2、Fsimage和Edits解析
代码语言:javascript复制1、NameNode被格式化之后,将在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current目录中产生如下文件:
## fsimage_0000000000000000000:
HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息。
## fsimage_0000000000000000000.md5
md5文件
## seen_txid
seen_txid文件保存的是一个数字,就是最后一个edits_的数字
## VERSION
版本文件
##
每次NameNode启动的时候都会将Fsimage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合并
3、查看Fsimage和Edits
代码语言:javascript复制? oiv查看Fsimage文件
## 语法
hdfs oiv -p 文件类型 -i镜像文件 -o 转换后文件输出路径
## 实操
hdfs oiv -p XML -i fsimage_0000000000000000025 -o ./fsimage.xml
代码语言:javascript复制? oev查看Edits文件
## 语法
hdfs oev -p 文件类型 -i编辑日志 -o 转换后文件输出路径
## 实操
hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o ./edits.xml
4、NameNode故障处理
代码语言:javascript复制?故障处理可以采用如下两种方法恢复数据
## 方法1:
将SecondaryNameNode中数据拷贝到NameNode存储数据的目录,并重新启动NameNode即可
## 方法2:
使用-importCheckpoint选项启动NameNode守护进程,从而将SecondaryNameNode中数据拷贝到NameNode目录中。
5、NameNode多目录配置
?==多目录配置:==NameNode的本地目录可以配置成多个,且每个目录存放内容相同,增加了可靠性
1、在hdfs-site.xml文件中添加配置
代码语言:javascript复制<!-- ${hadoop.tmp.dir}在core-site.xml中配置 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/name1,file:///${hadoop.tmp.dir}/dfs/name2</value>
</property>
2、停止集群,删除data和logs中所有数据
代码语言:javascript复制rm -rf /usr/hadoop/data/ /usr/hadoop/logs/
3、格式化集群并启动
代码语言:javascript复制hdfs namenode –format
start-dfs.sh
⭐️5、DataNode
1、DataNode工作机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fNCzNT0w-1617677655649)(D:Code_Study博客笔记Hadoop学习笔记.assets1604385624038.png)]
代码语言:javascript复制1、一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
2、DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。
3、心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
4、集群运行中可以安全加入和退出一些机器。
代码语言:javascript复制?注意: DataNode节点保证数据完整性的方法。
1、当DataNode读取Block的时候,它会计算CheckSum。
2、如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
3、Client读取其他DataNode上的Block。
4、DataNode在其文件创建后周期验证CheckSum,如图所示。
2、DataNode掉线时限参数设置
代码语言:javascript复制?hdfs-site.xml
<!-- heartbeat.recheck-interval的单位为毫秒,dfs.heartbeat.interval的单位为秒 -->
<property>
<name>dfs.namenode.heartbeat.recheck-interval</name>
<value>300000</value>
</property>
<property>
<name>dfs.heartbeat.interval</name>
<value>3</value>
</property>
代码语言:javascript复制1、DataNode进程死亡或者网络故障造成DataNode无法与NameNode通信。
2、NameNode不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长。
3、HDFS默认的超时时长为10分钟 30秒。 见上述xml配置
4、如果定义超时时间为TimeOut,则超时时长的计算公式为:
TimeOut = 2 * dfs.namenode.heartbeat.recheck-interval 10 * dfs.heartbeat.interval。
而默认的dfs.namenode.heartbeat.recheck-interval 大小为5分钟,dfs.heartbeat.interval默认为3秒。
3、数据节点黑白名单
代码语言:javascript复制添加白名单 :添加到白名单的主机节点,都允许访问NameNode,不在白名单的主机节点,都会被退出。
?操作步骤
## 在NameNode的/usr/hadoop/etc/hadoop目录下创建dfs.hosts文件
vim dfs.hosts
## 添加白名单主机名称
slave1
slave2
## 在NameNode的hdfs-site.xml配置文件中增加dfs.hosts属性
<property>
<name>dfs.hosts</name>
<value>/usr/hadoop/etc/hadoop/dfs.hosts</value>
</property>
## 配置文件分发
xsync hdfs-site.xml
## 刷新NameNode
hdfs dfsadmin -refreshNodes
## 更新ResourceManager节点
yarn rmadmin -refreshNodes
##如果数据不均衡,可以用命令实现集群的再平衡
start-balancer.sh
代码语言:javascript复制添加黑名单 :在黑名单上面的主机都会被强制退出。
?具体操作
## 在NameNode的/usr/hadoop/etc/hadoop目录下创建dfs.hosts.exclude文件
vi dfs.hosts.exclude
## 添加黑名单主机名
slave3
## 在NameNode的hdfs-site.xml配置文件中增加dfs.hosts.exclude属性
<property>
<name>dfs.hosts.exclude</name>
<value>/opt/module/hadoop-2.7.2/etc/hadoop/dfs.hosts.exclude</value>
</property>
## 刷新NameNode、ResourceManager
hdfs dfsadmin -refreshNodes
yarn rmadmin -refreshNodes
## 检查Web浏览器,退役节点的状态为decommission in progress(退役中),说明数据节点正在复制块到其他节点
## 如果数据不均衡,可以用命令实现集群的再平衡
start-balancer.sh
注意: 等待退役节点状态为decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。 如果副本数是3,服役的节点小于等于3,是不能退役成功的,需要修改副本数后才能退役
4、Datanode多目录配置
?==多目录配置:==每个目录存储的数据不一样。即:数据不是副本
1、在hdfs-site.xml文件中添加配置
代码语言:javascript复制<!-- ${hadoop.tmp.dir}在core-site.xml中配置 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
</property>
2、停止集群,删除data和logs中所有数据
代码语言:javascript复制rm -rf /usr/hadoop/data/ /usr/hadoop/logs/
3、格式化集群并启动
代码语言:javascript复制hdfs namenode –format
start-dfs.sh
⭐️6、HDFS2.x新特性
1、小文件存档
代码语言:javascript复制?HDFS存储小文件弊端
每个文件均按块存储,每个块的元数据存储在NameNode的内存中,因此HDFS存储小文件会非常低效。因为大量的小文件会耗尽NameNode中的大部分内存。
但注意,存储小文件所需要的磁盘容量和数据块的大小无关。例如,一个1MB的文件设置为128MB的块存储,实际使用的是1MB的磁盘空间,而不是128MB。
代码语言:javascript复制?解决存储小文件办法之一
HDFS存档文件或HAR文件,是一个更高效的文件存档工具,它将文件存入HDFS块,在减少NameNode内存使用的同时,允许对文件进行透明的访问。具体说来,HDFS存档文件对内还是一个一个独立文件,对NameNode而言却是一个整体,减少了NameNode的内存。
代码语言:javascript复制?实操
## 需要启动YARN进程
start-yarn.sh
## 把/user/input目录里面的所有文件归档成一个叫input.har的归档文件,并把归档后文件存储到/user/output路径下。
bin/hadoop archive -archiveName input.har –p /user/input /user/output
## 解归档文件
hadoop fs -cp har:///user/output/input.har/* /user
2、回收站
代码语言:javascript复制开启回收站功能,可以将删除的文件在不超时的情况下,恢复原数据,起到防止误删除、备份等作用。
? 参数说明
1、默认值fs.trash.interval=0,0表示禁用回收站;其他值表示设置文件的存活时间。
2、默认值fs.trash.checkpoint.interval=0,检查回收站的间隔时间。如果该值为0,则该值设置和fs.trash.interval的参数值相等。
3、要求fs.trash.checkpoint.interval<=fs.trash.interval。
代码语言:javascript复制配置core-site.xml
<!-- 配置垃圾回收时间为1分钟 -->
<property>
<name>fs.trash.interval</name>
<value>1</value>
</property>
<!-- 进入垃圾回收站用户名称,默认是dr.who,修改为roo用户 -->
<property>
<name>hadoop.http.staticuser.user</name>
<value>root</value>
</property>
代码语言:javascript复制路径
/user/root/.Trash/….
代码语言:javascript复制修改访问垃圾回收站用户名称,在core-site.xml中配置
==注意注意:==通过程序删除的文件不会经过回收站,需要调用moveToTrash()才进入回收站
Trash trash = New Trash(conf);
trash.moveToTrash(path);
代码语言:javascript复制恢复回收站数据
hadoop fs -mv /user/root/.Trash/Current/user/root/input /user/root/input
代码语言:javascript复制清空回收站
hadoop fs -expunge
3、快照管理
代码语言:javascript复制⭐️ 快照相当于对目录做一个备份。并不会立即复制所有文件,而是记录文件变化。
## 开启指定目录的快照功能
hdfs dfsadmin -allowSnapshot 路径
## 禁用指定目录的快照功能,默认是禁用
hdfs dfsadmin -disallowSnapshot 路径
## 对目录创建快照
hdfs dfs -createSnapshot 路径
## 指定名称创建快照
hdfs dfs -createSnapshot 路径 名称
## 重命名快照
hdfs dfs -renameSnapshot 路径 旧名称 新名称
## 列出当前用户所有可快照目录
hdfs lsSnapshottableDir
## 比较两个快照目录的不同之处
hdfs snapshotDiff 路径1 路径2
## 删除快照
hdfs dfs -deleteSnapshot <path> <snapshotName>