目录
[1.1 什么是HDFS](#1.1 什么是HDFS)
[1.2 HDFS的优点](#1.2 HDFS的优点)
[1.3.1、 NameNode](#1.3.1、 NameNode)
[1.3.2、 NameNode的职责](#1.3.2、 NameNode的职责)
[1.3.4、 DataNode的职责](#1.3.4、 DataNode的职责)
[1.3.5、Secondary NameNode](#1.3.5、Secondary NameNode)
[1.3.6、Secondary NameNode的职责](#1.3.6、Secondary NameNode的职责)
[2.2 、数据写入](#2.2 、数据写入)
[2.3、 数据读取](#2.3、 数据读取)
[2.4、 容错机制](#2.4、 容错机制)
1、HDFS简介
1.1 什么是HDFS
HDFS是Hadoop生态系统中的一个分布式文件系统,旨在在集群的廉价硬件上可靠地存储大数据集。HDFS设计为高容错,并为高吞吐量数据访问而优化,适用于在商用硬件上运行的大数据应用。
1.2 HDFS的优点
- 高容错性:数据通过副本机制存储在多个节点上,确保在硬件故障时数据的高可用性。
- 高吞吐量:通过批量处理大数据,HDFS优化了数据的读写速度。
- 可扩展性:通过添加节点,可以轻松扩展HDFS的存储容量和计算能力。
- 可靠性:通过分布式架构和数据冗余,确保数据在系统故障情况下的完整性和可用性。
1.3、HDFS的架构
HDFS采用主从架构,主要由NameNode和DataNode两类节点组成。
1.3.1、 NameNode
NameNode是HDFS的主节点,负责管理文件系统的命名空间和文件块的映射关系。它存储所有文件和目录的元数据(如文件名、权限、块位置等),并协调客户端对数据的访问请求。
1.3.2、 NameNode的职责
文件系统命名空间管理:管理文件和目录的结构,维护元数据。
- 块管理:管理文件与块的映射关系,以及块在DataNode上的存储位置。
- 集群管理:监控DataNode的健康状态,处理节点故障。
1.3.3、DataNode
DataNode是HDFS的工作节点,负责存储实际的数据块。每个DataNode定期向NameNode发送心跳信号,报告其健康状态和存储情况。
1.3.4、 DataNode的职责
- 数据存储:存储HDFS文件的数据块。
- 数据块报告:定期向NameNode发送数据块列表,报告其存储情况。
- 数据块操作:执行客户端请求的读写操作,负责数据块的创建、删除和复制。
1.3.5、Secondary NameNode
Secondary NameNode并不是NameNode的热备份,而是辅助NameNode进行元数据管理的节点。它定期获取NameNode的元数据快照并合并编辑日志,以减轻NameNode的负载。
1.3.6、Secondary NameNode的职责
- 元数据快照:定期从NameNode获取元数据快照。
- 合并编辑日志:将元数据快照与编辑日志合并,生成新的元数据文件,减轻NameNode的内存压力。
2、HDFS的工作原理
HDFS通过分布式存储和冗余机制,实现高可靠性和高可用性。以下是HDFS的几个关键工作原理。
2.1、文件存储
HDFS将文件分割成固定大小的块(3.x默认为128MB),并将这些块存储在不同的DataNode上。每个块会被复制到多个DataNode(默认3个副本),以确保数据的可靠性。
2.2 、数据写入
当客户端向HDFS写入数据时,数据首先被分割成块,并通过Pipeline机制写入到多个DataNode。具体步骤如下:
- 客户端请求NameNode:客户端向NameNode请求写入文件。
- NameNode分配块和DataNode:NameNode为文件分配数据块并选择存储这些块的DataNode。
- 客户端写入数据块:客户端将数据块写入第一个DataNode,第一个DataNode再将数据块复制到第二个DataNode,依此类推。
- 数据块确认:当所有副本写入成功后,客户端接收到确认消息,表示数据写入完成。
2.3、 数据读取
当客户端从HDFS读取数据时,NameNode提供数据块的位置信息,客户端直接从相应的DataNode读取数据。具体步骤如下:
- 客户端请求NameNode:客户端向NameNode请求读取文件。
- NameNode返回块位置:NameNode返回文件块所在的DataNode列表。
- 客户端读取数据块:客户端直接从DataNode读取数据块,并在本地合并这些数据块,恢复成完整的文件。
2.4、 容错机制
HDFS通过数据块副本机制实现容错。当DataNode发生故障时,NameNode会检测到该DataNode的心跳信号丢失,并在其他健康的DataNode上重新复制丢失的数据块。
2.5、元数据管理
NameNode负责管理文件系统的元数据,包括文件名、目录结构、权限和数据块位置等。为了保证元数据的一致性和持久性,NameNode将元数据存储在内存中,并定期写入到本地磁盘。
3、HDFS的Shell操作
hadoop fs 具体命令 OR hdfs dfs 具体命令
bash
#从本地粘贴到HDFS
hadoop fs -copyFromLocal a.txt /demo
hadoop fs -put a.txt /
#-rm -r:递归删除目录及目录里面内容;-rm:删除文件或文件夹
hadoop fs -rm -r /demo/a.txt
#-moveFromLocal:从本地剪切粘贴到HDFS
hadoop fs -moveFromLocal a.txt /demo
#-appendToFile:追加一个文件到已经存在的文件末尾
hadoop fs -appendToFile b.txt /demo/a.txt
#-copyToLocal:从HDFS拷贝到本地
hadoop fs -copyToLocal /demo/a.txt /usr/local/hadoop-3.4.0
hadoop fs -get /demo/a.txt /usr/local/hadoop-3.4.0
#显示目录
hadoop fs -ls /demo
#查看内容
hadoop fs -cat /demo/a.txt
#创建路径
hadoop fs -mkdir /demo
#复制
hadoop fs -cp /a.txt /demo/
#-tail:显示一个文件的末尾1kb的数据
hadoop fs -tail /demo/a.txt
#查看大小
hadoop fs -du -h /demo
#-setrep:设置HDFS中文件的副本数量,副本数超过DataNode时,并不会创建那么多副本,只有当有足够的节点数时才会创建
hadoop fs -setrep 2 /demo/a.txt
4、HDFS的API操作
4.1、配置客户端环境变量
配置环境变量
配置path变量
重启电脑使变量生效
4.2、客户端api的使用
java
package com.xiaojie.hadoop.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
@Component
@Slf4j
public class HdfsClientUtil {
@Value("${wssnail.hdfs.url}")
private String url;
@Value("${wssnail.hdfs.user-name}")
private String userName;
@Autowired
private Configuration configuration;
//创建文件
public void mkDirs() throws URISyntaxException, IOException, InterruptedException {
//获取文件系统
FileSystem fs = FileSystem.get(new URI(url), configuration, userName);
// 2 创建目录
fs.mkdirs(new Path("/hello/world"));
// 3 关闭资源
fs.close();
log.info("创建目录成功>>>>>>>>>>>>>>>");
}
//上传文件
public void putFile() throws URISyntaxException, IOException, InterruptedException {
//设置副本数
configuration.set("dfs.replication", "2");
FileSystem fs = FileSystem.get(new URI(url), configuration, userName);
// 2 上传文件
fs.copyFromLocalFile(new Path("d:/hello.txt"), new Path("/"));
// 3 关闭资源
fs.close();
}
//下载文件
public void downloadFile() throws URISyntaxException, IOException, InterruptedException {
// 1 获取文件系统
FileSystem fs = FileSystem.get(new URI(url), configuration, userName);
// 2 执行下载操作
// boolean delSrc 指是否将原文件删除
// Path src 指要下载的文件路径
// Path dst 指将文件下载到的路径
// boolean useRawLocalFileSystem 是否开启文件校验
fs.copyToLocalFile(false, new Path("/hello.txt"), new Path("d:/hello1.txt"), true);
// 3 关闭资源
fs.close();
}
//移动
public void renameFile() throws URISyntaxException, IOException, InterruptedException {
// 1 获取文件系统
FileSystem fs = FileSystem.get(new URI(url), configuration, userName);
// 2 修改文件名称
fs.rename(new Path("/hello.txt"), new Path("/hello1.txt"));
// 3 关闭资源
fs.close();
}
//删除
public void deleteFile() throws URISyntaxException, IOException, InterruptedException {
// 1 获取文件系统
FileSystem fs = FileSystem.get(new URI(url), configuration, userName);
// 2 删除
fs.delete(new Path("/hello1.txt"), true);
// 3 关闭资源
fs.close();
}
//查看
public void listFiles() throws URISyntaxException, IOException, InterruptedException {
FileSystem fs = FileSystem.get(new URI(url), configuration, userName);
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
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("文件修改时间:" + simpleDateFormat.format(fileStatus.getModificationTime()));
System.out.println("副本数:" + fileStatus.getReplication());
System.out.println("blockSize: " + fileStatus.getBlockSize() / 1024 / 1024 + "M");
System.out.println("文件名称信息:" + fileStatus.getPath().getName());
// 获取块信息
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
System.out.println("块信息:" + Arrays.toString(blockLocations));
}
// 3 关闭资源
fs.close();
}
//文件文件夹判断
public void isFile() throws URISyntaxException, IOException, InterruptedException {
FileSystem fs = FileSystem.get(new URI(url), configuration, userName);
// 2 判断是文件还是文件夹
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for (FileStatus fileStatus : listStatus) {
// 如果是文件
if (fileStatus.isFile()) {
System.out.println("文件名称:" + fileStatus.getPath().getName());
} else {
System.out.println("目录名称:" + fileStatus.getPath().getName());
}
}
// 3 关闭资源
fs.close();
}
}
完整代码:见附录
5、参考
https://blog.csdn.net/weixin_42175752/article/details/140097992
6、附录
1、window环境下的hadoop依赖
链接: https://pan.baidu.com/s/1nFIMXRlVpOrt5ahenq_Prg?pwd=c23a