Hadoop之HDFS
HDFS概述
HDFS产生背景
随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统,HDFS只是分布式文件管理系统中的一种
HDFS定义
Hadoop Distributed File System ,它是一个文件系统,用于存储文件,通过目录树来定位文件,其次,他是分布式的,由很多服务器联合起来实现功能,集群中的服务器有各自的角色,HDFS使用场景:适合一次写入,多次读出的场景,且不支持文件的修改。适合用来做数据分析
HDFS优缺点
HDFS组成架构
HDFS文件块大小
HDFS的文件在物理上时分块存储,块的大小可以通过配置参数dfs.blocksize来规定,默认大小的hadoop2.x版本中是128m,老版本是64m
HDFS的shell操作
基本语法
- hadoop fs 具体命令
- hdfs dfs 具体命令
常用命令实操
准备工作
# 启动hdfs集群
[atguigu@hadoop102 hadoop-3.1.3]$ start-dfs.sh
Starting namenodes on [hadoop102]
Starting datanodes
Starting secondary namenodes [hadoop104]
[atguigu@hadoop102 hadoop-3.1.3]$ my_jps.sh
hadoop102,hadoop103,hadoop104进程启动情况
=================hadoop102 jps情况=================
1553 DataNode
1427 NameNode
1833 Jps
=================hadoop103 jps情况=================
1316 Jps
1245 DataNode
=================hadoop104 jps情况=================
1335 SecondaryNameNode
1418 Jps
1259 DataNode
上传
# -copyFromLocal:从本地服务器中拷贝到hdfs
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -copyFromLocal ./shuguo.txt /sanguo
# -moveFromLocal:从本地服务器中剪切到hdfs
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -moveFromLocal ./guanyu.txt /sanguo
# -appendToFile:追加一个文件到已经存在的文件末尾
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -appendToFile guanyu-zhuijia.txt /sanguo/guanyu.txt
# -put:等同于copyFormLocal
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -put guanyu-zhuijia.txt /sanguo
下载
# -copyToLocal:从HDFS拷贝到本地
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -copyToLocal /sanguo/guanyu.txt ./
# -get等同于copyToLocal,就是从HDFS下载文件到本地
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -get /sanguo/guanyu.txt ./
# -getmerge 合并下载多个文件
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -getmerge /sanguo/guanyu.txt /sanguo/shuguo.txt ./xiongdi.txt
HDFS直接操作
1)-ls: 显示目录信息
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -ls /
2)-mkdir:在HDFS上创建目录
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -mkdir -p /sanguo/shuguo
3)-cat:显示文件内容
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -cat /sanguo/shuguo/kongming.txt
4)-chgrp 、-chmod、-chown:Linux文件系统中的用法一样,修改文件所属权限
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -chmod 666 /sanguo/shuguo/kongming.txt
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -chown atguigu:atguigu /sanguo/shuguo/kongming.txt
5)-cp :从HDFS的一个路径拷贝到HDFS的另一个路径
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -cp /sanguo/shuguo/kongming.txt /zhuge.txt
6)-mv:在HDFS目录中移动文件
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -mv /zhuge.txt /sanguo/shuguo/
7)-tail:显示一个文件的末尾1kb的数据
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -tail /sanguo/shuguo/kongming.txt
8)-rm:删除文件或文件夹
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -rm /sanguo/guanyu.txt
9)-rmdir:删除空目录
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -mkdir /test
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -rmdir /test
10)-du统计文件夹的大小信息
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -du -s -h /user/atguigu/test
2.7 K /user/atguigu/test
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -du -h /user/atguigu/test
1.3 K /user/atguigu/test/README.txt
15 /user/atguigu/test/jinlian.txt
1.4 K /user/atguigu/test/zaiyiqi.txt
11)-setrep:设置HDFS中文件的副本数量
[atguigu@hadoop102 hadoop-3.1.3]$ hadoop fs -setrep 10 /sanguo/shuguo/kongming.txt
HDFS客户端操作
JAVA客户端操作HDFS
HDFS客户端环境准备
- 在windows环境中添加Hadoop相关的依赖,配置Hadoop的环境变量
- HADOOP_HOME
- Path
- 把hadoop环境目录下的hadoop.dll和winutils.exe放在系统盘的/windows/System32下
- 执行java代码,如果不成功,重启电脑试试
添加hdfs的maven依赖
<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”
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="error" strict="true" name="XMLConfig">
<Appenders>
<!-- 类型名为Console,名称为必须属性 -->
<Appender type="Console" name="STDOUT">
<!-- 布局为PatternLayout的方式,
输出样式为[INFO] [2018-01-22 17:34:01][org.test.Console]I'm here -->
<Layout type="PatternLayout"
pattern="[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n" />
</Appender>
</Appenders>
<Loggers>
<!-- 可加性为false -->
<Logger name="test" level="info" additivity="false">
<AppenderRef ref="STDOUT" />
</Logger>
<!-- root loggerConfig设置 -->
<Root level="info">
<AppenderRef ref="STDOUT" />
</Root>
</Loggers>
</Configuration>
测试连接
/**
* 获取Hdfs客户端连接
*/
@Test
public void testCreateHdfsClient() throws IOException, InterruptedException {
URI uri = URI.create("hdfs://hadoop102:9820");
Configuration configuration = new Configuration();
String user = "atguigu";
//获取Hdfs客户端连接
FileSystem fileSystem = FileSystem.get(uri, configuration, user);
System.out.println(fileSystem.getClass().getName()); //测试ok
fileSystem.close();
}
HDFS文件上传
private FileSystem fs;
/**
* 获取Hdfs客户端连接
*/
@Before
public void init() throws IOException, InterruptedException {
URI uri = URI.create("hdfs://hadoop102:9820");
Configuration configuration = new Configuration();
String user = "atguigu";
//获取Hdfs客户端连接
fs = FileSystem.get(uri, configuration, user);
System.out.println(fs.getClass().getName());
}
/**
* 关闭资源
* @throws IOException
*/
@After
public void after() throws IOException{
fs.close();
}
@Test
public void testCopyFromLocal () throws IOException {
fs.copyFromLocalFile(false,
true,
new Path("D:/二次剪辑素材/hello.txt"),
new Path("/java-api/upload/"));
}
配置:配置的优先级 Configuration > hdfs-site.xml > hdfs-default.xml
HDFS文件下载
@Test
public void testCopyToLocal () throws IOException {
fs.copyToLocalFile(false,
new Path("/java-api/upload/hello.txt"),
new Path("C:/Users/Administrator.DESKTOP-FLGB82I/Desktop/大数据学习笔记/"),
true);
// 注意第四个参数为false时会下载文件的检验核,是一串16位进制的数据
}
HDFS删除文件和目录
@Test
public void testDelete() throws IOException {
fs.delete(new Path("/java-api/upload/hello.txt"),true);
}
HDFS文件更名和移动
@Test
public void testRename() throws IOException {
//移动文件
fs.rename(new Path("/java-api/upload/hello.txt"),
new Path("/java-api/rename/"));
//改名
fs.rename(new Path("/java-api/rename/hello.txt"),
new Path("/java-api/rename/hello_rename.txt"));
}
HDFS文件详情查看
@Test
public void testListFiles() throws IOException {
RemoteIterator<LocatedFileStatus> files = fs.listFiles(new Path("/"), true);
while (files.hasNext()){
LocatedFileStatus next = files.next();
System.out.println("文件名:"+next.getPath().getName());
System.out.println("块大小:"+next.getBlockSize());
System.out.println("副本数:"+next.getReplication());
System.out.println("权限信息:"+next.getPermission());
}
}
HDFS文件和文件夹判断
@Test
public void testListStatus() throws IOException {
FileStatus[] fileStatuses = fs.listStatus(new Path("/java-api/rename"));
for (FileStatus status:fileStatuses) {
if(status.isDirectory()){
System.out.println("DIR:"+status.getPath().getName());
}else{
System.out.println("FILE:"+status.getPath().getName());
}
}
}
HDFS的数据流
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步)。
思考
- HDFS根据请求返回DataNode节点的策略? -- 机架感知
-- 如果当前Client所在机器有DataNode节点,那就返回当前机器DN1,否则从集群中随机一台
-- 根据第一台机器的位置,然后再其他机架上随机一台,在第二台机器所在机架上再随机一台
-- 以上策略的缘由:为了提高数据的可靠性,同时一定程度也保证数据传输的效率
-- 客户端建立传输通道的时候如果确定和哪一台DataNode先建立连接? -- 网络拓扑
-- 找离client最近的一台机器先建立通道(网络距离)
-- client为什么是以串行的方式建立通道
-- 本质上就是为了降低client的IO开销
-- 数据传输的时候如果保证数据成功?
-- 采用了ack回执的策略保证了数据完整成功上传
HDFS读数据流程
(1)客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的 DataNode地址。
(2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
(4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
NameNode和SecondaryNameNode
NN和2NN工作机制
1.元数据信息要保存在哪
- 保存到磁盘
- 不足:读写速度慢,效率低
- 保存到内存
- 不足:数据不安全(断电丢失)
- 最终解决方案: 磁盘 + 内存
2.元数据在磁盘与内存中如何进行同步(元数据的维护策略)
- 当我们对元数据进行操作的时候,首先在内存中进行合并,其次还要把相关的操作记录追加到edits编辑日志文件 中,在满足一定条件下进行edits文件中的记录合并到元数据信息文件中fsimage
3.谁负责对NN的元数据信息进行合并?
- 2NN主要负责对NN的元数据进行合并,当满足一定条件下,2NN会监测本地时间,每隔一个小时会主动对NN的 edits文件和fsimage文件进行合并,合并的时候,首先会通知NN,这个时候NN就会停止对正在使用的edits文 件的追加。同时会新建一个新的edits编辑日志文件,保证NN的正常工作。接下来会把NN本地的fsimage文件和 edits编辑日志拉取到2NN本地,在内存中对二者进行合并,最后产生最新的fsimage文件,把最新的fsimage文 件发送给NN的本地。注意,还有一个情况,当NN的edits文件中的操作次数累计达到100万次,即便还没到1小时, 2NN也会进行合并。(2NN会没隔60秒监测一次NN方的edits文件的操作次数),
2NN也会自己把最新的fsimage文件备份一份
Fsimage和Edits解析
oiv查看Fsimage文件
[atguigu@hadoop102 current]$ pwd
/opt/module/hadoop-3.1.3/data/dfs/name/current
[atguigu@hadoop102 current]$ hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/module/hadoop-3.1.3/fsimage.xml
[atguigu@hadoop102 current]$ cat /opt/module/hadoop-3.1.3/fsimage.xml
oev查看Edits文件
[atguigu@hadoop102 current]$ hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-3.1.3/edits.xml
[atguigu@hadoop102 current]$ cat /opt/module/hadoop-3.1.3/edits.xml
[atguigu@hadoop102 current]$ sz edits.xml # 下载到本地
CheckPoint时间设置
1)通常情况下,SecondaryNameNode每隔一小时执行一次
hdfs-default.xml
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600s</value>
</property>
2)一分钟检查一次操作次数,当操作次数达到1百万时,SecondaryNameNode执行一次
<property>
<name>dfs.namenode.checkpoint.txns</name>
<value>1000000</value>
<description>操作动作次数</description>
</property>
<property>
<name>dfs.namenode.checkpoint.check.period</name>
<value>60s</value>
<description> 1分钟检查一次操作次数</description>
</property >
集群安全模式
DataNode
DataNode工作机制
(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
(2)DataNode启动后向NameNode注册,通过后,周期性(1小时)的向NameNode上报所有的块信息。
(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
(4)集群运行中可以安全加入和退出一些机器。
数据完整性
思考:如果电脑磁盘里面存储的数据是控制高铁信号灯的红灯信号(1)和绿灯信号(0),但是存储该数据的磁盘坏了,一直显示是绿灯,是否很危险?同理DataNode节点上的数据损坏了,却没有发现,是否也很危险,那么如何解决呢?
如下是DataNode节点保证数据完整性的方法。
(1)当DataNode读取Block的时候,它会计算CheckSum。
(2)如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
(3)Client读取其他DataNode上的Block。
(4)常见的校验算法 crc(32),md5(128),sha1(160)
(5)DataNode在其文件创建后周期验证CheckSum。
掉线时限参数设置
NN没有接受到DN时间可容许时间设置
需要注意的是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>
服役新数据节点
1)需求
随着公司业务的增长,数据量越来越大,原有的数据节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点。
2)环境准备
(1)在hadoop104主机上再克隆一台hadoop105主机
(2)修改IP地址和主机名称
(3)删除原来HDFS文件系统留存的文件(/opt/module/hadoop-3.1.3/data和logs)
(4)source一下配置文件
[atguigu@hadoop105 hadoop-3.1.3]$ source /etc/profile
(4)单节点启动datanode
[atguigu@hadoop105 hadoop-3.1.3]$ hdfs --daemon start datanode
刷新网页查看datanode集群
退役旧数据节点
添加白名单和黑名单
白名单和黑名单是hadoop管理集群主机的一种机制。
添加到白名单的主机节点,都允许访问NameNode,不在白名单的主机节点,都会被退出。添加到黑名单的主机节点,不允许访问NameNode,会在数据迁移后退出。
实际情况下,白名单用于确定允许访问NameNode的DataNode节点,内容配置一般与workers文件内容一致。 黑名单用于在集群运行过程中退役DataNode节点。
配置白名单和黑名单的具体步骤如下:
1)在NameNode节点的/opt/module/hadoop-3.1.3/etc/hadoop目录下分别创建whitelist 和blacklist文件
[atguigu@hadoop102 hadoop]$ pwd
/opt/module/hadoop-3.1.3/etc/hadoop
[atguigu@hadoop102 hadoop]$ touch whitelist
[atguigu@hadoop102 hadoop]$ touch blacklist
在whitelist中添加如下主机名称,假如集群正常工作的节点为102 103 104 105
hadoop102
hadoop103
hadoop104
hadoop105
# 黑名单暂时为空。
2)在hdfs-site.xml配置文件中增加dfs.hosts和 dfs.hosts.exclude配置参数
<!-- 白名单 -->
<property>
<name>dfs.hosts</name>
<value>/opt/module/hadoop-3.1.3/etc/hadoop/whitelist</value>
</property>
<!-- 黑名单 -->
<property>
<name>dfs.hosts.exclude</name>
<value>/opt/module/hadoop-3.1.3/etc/hadoop/blacklist</value>
</property>
3)分发配置文件whitelist,blacklist,hdfs-site.xml (注意:105节点也要发一份)
[atguigu@hadoop102 etc]$ xsync hadoop/
[atguigu@hadoop102 etc]$ rsync -av hadoop/ atguigu@hadoop105:/opt/module/hadoop-3.1.3/etc/hadoop/
4)重新启动集群(注意:105节点没有添加到workers,因此要单独起停)
[atguigu@hadoop102 hadoop-3.1.3]$ stop-dfs.sh
[atguigu@hadoop102 hadoop-3.1.3]$ start-dfs.sh
[atguigu@hadoop105 hadoop-3.1.3]$ hdfs –daemon start datanode
5)在web浏览器上查看目前正常工作的DN节点
黑名单退役
1)编辑/opt/module/hadoop-3.1.3/etc/hadoop目录下的blacklist文件
[atguigu@hadoop102 hadoop] vim blacklist
添加如下主机名称(要退役的节点)
hadoop105
2)分发blacklist到所有节点
[atguigu@hadoop102 hadoop]$ my_rsync ./blacklist
[atguigu@hadoop102 hadoop]$ scp -r ./blacklist atguigu@hadoop105:/opt/module/hadoop/
3)刷新NameNode、刷新ResourceManager
[atguigu@hadoop102 hadoop-3.1.3]$ hdfs dfsadmin -refreshNodes
Refresh nodes successful
[atguigu@hadoop102 hadoop-3.1.3]$
4)检查Web浏览器,退役节点的状态为decommission in progress(退役中),说明数据节点正在复制块到其他节点
5)等待退役节点状态为decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。注意:如果副本数是3,服役的节点小于等于3,是不能退役成功的,需要修改副本数后才能退役
此外,还有一种白名单退役方式,就是直接从白名单中移除某台机器即可,然后再刷新集群
hdfs dfsadmin -refreshNodes,执行后发现105果然移除了,并且直接杀死了,不推荐这种做法
DataNode多目录配置
DataNode可以配置成多个目录,每个目录存储的数据不一样。即:数据不是副本
具体配置
(1)在hdfs-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中所有数据。
[atguigu@hadoop102 hadoop-3.1.3]$ rm -rf data/ logs/
[atguigu@hadoop103 hadoop-3.1.3]$ rm -rf data/ logs/
[atguigu@hadoop104 hadoop-3.1.3]$ rm -rf data/ logs/
(3)格式化集群并启动。
[atguigu@hadoop102 hadoop-3.1.3]$ bin/hdfs namenode –format
[atguigu@hadoop102 hadoop-3.1.3]$ sbin/start-dfs.sh
(4)查看结果
[atguigu@hadoop102 dfs]$ ll
总用量 12
drwx------. 3 atguigu atguigu 4096 4月 4 14:22 data1
drwx------. 3 atguigu atguigu 4096 4月 4 14:22 data2
drwxrwxr-x. 3 atguigu atguigu 4096 12月 11 08:03 name1
drwxrwxr-x. 3 atguigu atguigu 4096 12月 11 08:03 name2