概述
Hadoop Distributed File System,简称 HDFS,是一个分布式文件系统;
NameNode:存储文件的元数据,如文件名、文件目录结构、文件属性,以及每个文件的快列表和快所在的 DataNode;
DataNode:在本地文件系统存储文件快数据,以及块数据的校验和;
Secondary NameNode:每隔一段时间对 NameNode 元数据备份;
优缺点
优点
高容错率:
数据自动保存多个副本。它通过增加副本的形式提高容错性。
某一个副本丢失以后,它可以自动恢复。
适合处理大数据
数据规模:能够处理数据规模达到 GB、TB、甚至 PB 级别的数据;
文件规模:能够处理百万规模以上的文件数量,数量相当之大。
可构建在廉价机器上,通过多副本机制,提高可靠性。
缺点
不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
无法高效的对大量小文件进行存储。
存储大量小文件的话,它会占用 NameNode 大量的内存来存储文件目录和块信息。这样是不可取的,因为 NameNode 的内存总是有限的;
小文件存储的寻址时间会超过读取时间,它违反了 HDFS 的设计目标。
不支持并发写入、文件随机修改。
一个文件只能有一个写,不允许多个线程同时写
仅支持数据 append(追加),不支持文件的随机修改。
HDFS 组成架构
官方参考链接:https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Introduction
NameNode (nn):就是 Master,它是一个主管、管理者。
管理 HDFS 的名称空间;
配置副本策略;
管理数据块(Block)映射信息;
处理客户端读写请求。
DataNode:就是 Slave。NameNode 下达命令,DataNode 执行实际的操作。
存储实际的数据块;
执行数据块的读/写操作。
Secondary NameNode:并非 NameNode 的热备。当 NameNode 挂掉的时候,它并不能马上替换 NameNode 并提供服务。
辅助 NameNode,分担其工作量,比如定期合并 Fsimage 和 Edits,并推送给 NameNode ;
在紧急情况下,可辅助恢复 NameNode。
Client:就是客户端。
文件切分。文件上传 HDFS 的时候,Client 将文件切分成一个一个的 Block,然后进行上传;
与 NameNode 交互,获取文件的位置信息;
与 DataNode 交互,读取或者写入数据;
Client 提供一些命令来管理 HDFS,比如 NameNode 格式化命令;
Client 可以通过一些命令来访问 HDFS,比如对 HIDFS 增删查改操作;
HDFS 配置文件优先级
代码中 Configuration 对象中配置 > 在项目资源目录下得配置文件 > hdfs-site.xml > hdfs-default.xml
HDFS 文件块大小
HDFS 中的文件在物理上是分块存储(Block),块的大小可以通过 hdfs-default.xml 中配置参数 dfs.blocksize 配置,默认大小在 Hadoop2.x/3.x 版本中是 128M,1.x 版本中是 64M;
注:hdfs-default.xml 在 $HADOOP_HOME/share/hadoop/hdfs 目录下的 hadoop-hdfs-3.2.3.jar
JAR 内,需要解压才能查看;
思考:为什么块的大小不能设置太小,也不能设置太大?
HDFS 的块设置太小,会增加寻址时间,程序一直在找块的开始位置;
如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块升始位置所需的时间。导致程序在处理这块数据时,会非常慢。
总结:HDFS 块的大小设置主要取决于磁盘传输速率。
HDFS 的 Shell 操作
格式
hadoop fs 具体命令
hdfs dfs 具体命令
# HDFS 直接操作
## 创建文件夹(保持完整文件目录)
hadoop fs -mkdir /文件夹名称
示例:hadoop fs -mkdir /sanguo
## 查询目录下所有文件夹和文件信息
hadoop fs -ls 文件夹全路径
示例:hadoop fs -ls /
## 显示文件内容
hadoop fs -cat 文件全路径
示例:hadoop fs -cat /sanguo/shuguo.txt
## 修改文件所述权限(-chgrp/-chmod/-chown,和linux系统中的用法一样)
示例:hadoop fs -chmod 777
## 从 HDFS 的一个路径拷贝到 HDFS 的另一个路径
hadoop fs -cp 原全路径 目标全路径
示例:hadoop fs -cp /sanguo/shuguo.txt /jinguo
## 在 HDFS 目录中移动文件
hadoop fs -mv 原全路径 目标路径
示例:hadoop fs -mv /sanguo/wuguo.txt /jinguo
## 显示一个文件的末尾 1kb 的数据
hadoop fs -tail 文件全路径
示例:hadoop fs -tail /jinguo/shuguo.txt
## 删除文件或者文件夹
hadoop fs -rm 文件全路径或者文件夹全路径
示例:hadoop fs -rm /jinguo/shuguo.txt
## 递归删除目录及目录里的内容
hadoop fs -rm -r 文件夹全路径
示例:hadoop fs -rm -r /sanguo
## 统计文件夹的大小信息
hadoop fs -du 文件夹全路径
示例:hadoop fs -du /jinguo
## 设置 HDFS 中文件的副本数量
hadoop fs -setrep 副本数量 文件全路径
示例:hadoop fs -setrep 10 /jinguo/wuguo.txt
# 上传
## 从本地剪切粘贴到 HDFS 中
hadoop fs -moveFromLocal 文件 目标文件夹
示例: hadoop fs -moveFromLocal shuguo.txt /sanguo
## 从本地文件系统中拷贝文件到 HDFS 中(put 相当于 copyFromLocal)
hadoop fs -put 文件 目标文件夹
示例:hadoop fs -put wuguo.txt /sanguo
hadoop fs -copyFromLocal 文件 目标文件夹
示例:hadoop fs -copyFromLocal weiguo.txt /sanguo
## 最佳一个文件到已经存在的文件末尾
hadoop fs -appendToFile 文件 目标文件
示例:hadoop fs -appendToFile liubei.txt /sanguo/shuguo.txt
# 下载
## 从 HDFS 拷贝到本地
hadoop fs -copyToLocal 文件全路径 目标目录
示例:hadoop fs -copyToLocal /sanguo/shuguo.txt .
hadoop fs -get 文件全路径 目标目录
示例:hadoop fs -get /sanguo/shuguo.txt .
复制代码
HDFS API 操作
maven 依赖
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-client -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.2.3</version>
</dependency>
复制代码
具体操作
package top.simba1949;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.net.URI;
import java.util.Arrays;
/**
* 客户端代码步骤
* 1. 获取客户端对象(和 NameNode 连接)
* 2. 执行相关命令
* 3. 关闭资源
* @author SIMBA1949
* @date 2022/7/12
*/
@Slf4j
public class Application {
public static void main(String[] args) throws Exception {
// hadoop 集群中 NameNode 地址
URI uri = new URI("hdfs://hadoop1:8020");
// 配置文件信息
Configuration configuration = new Configuration();
// 获取到客户端对象
FileSystem fileSystem = FileSystem.get(uri, configuration, "root");
// 操作命令
fileOrFolder(fileSystem);
// 关闭资源
fileSystem.close();
}
/**
* 创建文件夹
* @throws Exception
*/
public static void mkdirs(FileSystem fileSystem) throws Exception {
// 创建文件夹命令
fileSystem.mkdirs(new Path("/xiyou/huoguoshan"));
}
/**
* 上传文件
* @param fileSystem
*/
public static void copyFromLocalFile(FileSystem fileSystem) throws Exception {
// 第一个参数:表示是否删除原数据,第二个参数:表示是否允许覆盖
// 第三个参数:表示源路径,第四个参数:表示目标路径
Path src = new Path("D:/BigData/hdfs-test-workspace/sunwukong.txt");
Path dst = new Path("/xiyou/huoguoshan");
fileSystem.copyFromLocalFile(true, true, src, dst);
}
/**
* 下载文件
* @param fileSystem
* @throws Exception
*/
public static void copyToLocalFile(FileSystem fileSystem) throws Exception {
Path src = new Path("/xiyou/huoguoshan/hdfs-test-workspace"); // 源路径
Path dst = new Path("D:/BigData/"); // 目标路径
// 第一个参数:表示是否删除
// 第二个参数:表示源路径,第三个参数:表示目标路径
// 第四个参数:表示开启本地校验
fileSystem.copyToLocalFile(true, src, dst, true);
}
/**
* 删除文件
* @param fileSystem
* @throws Exception
*/
public static void delete(FileSystem fileSystem) throws Exception {
// 第一个参数:表示删除的路径
// 第二个参数:表示是否递归删除
boolean blDelete = fileSystem.delete(new Path("/xiyou"), true);
if (blDelete){
log.info("/xiyou 已经删除成功");
}
}
/**
* 重命名
* @param fileSystem
* @throws Exception
*/
public static void rename(FileSystem fileSystem) throws Exception {
Path src = new Path("/input/word.txt"); // 源路径
Path dst = new Path("/input/word-copy.txt"); // 目标路径
// 第一个参数:表示源路径,第二个参数:表示目标路径
fileSystem.rename(src, dst);
}
/**
* 移动并重命名
* @param fileSystem
* @throws Exception
*/
public static void moveAndRename(FileSystem fileSystem) throws Exception {
Path src = new Path("/input/word-copy.txt"); // 源路径
Path dst = new Path("/word-copy-copy.txt"); // 目标路径
// 第一个参数:表示源路径,第二个参数:表示目标路径
fileSystem.rename(src, dst);
}
/**
* 获取文件信息
* @param fileSystem
* @throws Exception
*/
public static void getFileInfo(FileSystem fileSystem) throws Exception {
// 第一个参数:表示目标路径,第二个参数:表示是否递归
RemoteIterator<LocatedFileStatus> listFilesIterator = fileSystem.listFiles(new Path("/"), true);
while (listFilesIterator.hasNext()) {
LocatedFileStatus locatedFileStatus = listFilesIterator.next();
log.info("the file what [{}]", locatedFileStatus);
BlockLocation[] blockLocations = locatedFileStatus.getBlockLocations();
log.info("the file block what [{}]", Arrays.toString(blockLocations));
}
}
/**
* 文件、文件夹的判断
* @param fileSystem
* @throws Exception
*/
public static void fileOrFolder(FileSystem fileSystem) throws Exception {
// 第一个参数:表示目标路径
FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
for (FileStatus fileStatus : fileStatuses) {
String name = fileStatus.getPath().getName();
if (fileStatus.isFile()){
log.info("the path is file what [{}]", name);
}else {
log.info("the path is folder what [{}]", name);
}
}
}
}
复制代码
HDFS 的写数据流程
网络拓扑——节点距离计算
在 HDFS 写数据的过程中,NameNode 会选择距离待上传数据最近距离的 DataNode 接收数据。节点距离:两个节点到达最近的共同祖先的距离总和。
机架感知(副本存储节点选择)
机架感知官方说明:https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication
第一个副本在 Client 所处的节点上。如果客户端在集群外,随机选一个。
第二个副本在另一个机架的随机一个节点
第三个副本在第二个副本所在机架的随机节点
HDFS 的读数据流程
NN 和 2NN 的工作机制
Fsimage 和 Edits 解析
NameNode 被格式化之后,将在 ${HAOOP_HOME}/data/dfs/name/current
目录中产生如下文件
fsimage_oooooooooooooooooo0
fsimage_ooo00000ooooo000000. md5
seen_txid
VERSION
复制代码
Fsimage 文件: HDFS 文件系统元数据的一个永久性的检查点,其中包含 HDFS 文件系统的所有目录和文件 inode 的序列化信息。
Edits 文件:存放 HDFS 文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到 Edits 文件中。
seen_txid
文件保存的是一个数字,就是最后一个edits_
的数字
每次 NameNode 启动的时候都会 将 Fsimage 文件读入内存,加载 Edits 里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成 NameNode 启动的时候就将 Fsimage 和 Edits 文件进行了合并。
查看文件命令
# 基本语法
## 查看镜像文件
hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径
示例:
cd /opt/bigdata/hadoop-3.2.3/data/dfs/name/current/
hdfs oiv -p XML -i fsimage_0000000000000000256 -o /opt/fsimage.xml
## 查看编辑日志
hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径
示例:
cd /opt/bigdata/hadoop-3.2.3/data/dfs/name/current/
hdfs oev -p XML -i edits_0000000000000000001-0000000000000000154 -o /opt/edits.xml
复制代码
CheckPoint 设置
CheckPoint 配置在hdfs-default.xml
文件中。1)通常情况下,SecondaryNameNode 每隔一小时执行一次
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600</value>
<description>
The number of seconds between two periodic checkpoints.
Support multiple time unit suffix(case insensitive), as described
in dfs.heartbeat.interval.If no time unit is specified then seconds
is assumed.
</description>
</property>
复制代码
2)一分钟检查一次操作次数,当操作次数达到一百万次时,SecondaryNameNode 执行一次
<property>
<name>dfs.namenode.checkpoint.txns</name>
<value>1000000</value>
<description>The Secondary NameNode or CheckpointNode will create a checkpoint
of the namespace every 'dfs.namenode.checkpoint.txns' transactions, regardless
of whether 'dfs.namenode.checkpoint.period' has expired.
操作次数
</description>
</property>
<property>
<name>dfs.namenode.checkpoint.check.period</name>
<value>60</value>
<description>The SecondaryNameNode and CheckpointNode will poll the NameNode
every 'dfs.namenode.checkpoint.check.period' seconds to query the number
of uncheckpointed transactions. Support multiple time unit suffix(case insensitive),
as described in dfs.heartbeat.interval.If no time unit is specified then
seconds is assumed.
每分钟检查操作次数
</description>
</property>
复制代码
DataNode 工作机制
一个数据块在 DataNode 上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
DataNode 启动后向 NameNode 注册,注册成功后周期性(默认 6 小时)的向 NameNode 上报所有的块信息;
<!--DN向NN汇报当前解读信息的时间间隔,默认6小时-->
<property>
<name>dfs.blockreport.intervalMsec</name>
<value>21600000</value>
<description>Determines block reporting interval in milliseconds.</description>
</property>
<!--DN扫描自己节点块信息列表的时间,默认6小时-->
<property>
<name>dfs.datanode.directoryscan.interval</name>
<value>21600</value>
<description>Interval in seconds for Datanode to scan data directories and
reconcile the difference between blocks in memory and on the disk.
Support multiple time unit suffix(case insensitive), as described
in dfs.heartbeat.interval.If no time unit is specified then seconds
is assumed.
</description>
</property>
复制代码
心跳是每 3 秒一次,心跳返回结果带有 NameNode 给该 DataNode 的命令(如复制块数据到另一台机器,或删除某个数据块)。如果超过 10 分钟没有收到某个 DataNode 的心跳,则认为该节点不可用。
集群运行中可以安全加入和退出一些机器。
数据完整性
DataNode 节点保证数据完整性的方法。
当 DataNode 读取 Block 的时候,它会计算 CheckSum。
如果计算后的 CheckSum,与 Block 创建时值不一样,说明 Block 已经损坏。
Client 读取其他 DataNode 上的 Block。
常见的校验算法 crc (32) , md5(128), shal ( 160)。
DataNode 在其文件创建后周期验证 CheckSum。
掉线时限参数设置
DataNode 进程死亡或者网络故障造成 DataNode 无法与 NameNode 通信
NameNode 不会立即把该节点判定为死亡,要经过一段时间,这段时间暂称作超时时长。
HDFS 默认的超时时长为 10 分钟+30 秒。
如果定义超时时间为 TimeOut,则超时时长的计算公式为:
TimeOut = 2*dfs.namenode.heartbeat.recheck-interval+10* dfs.heartbeat.interval
而默认的dfs.namenode.heartbeat.recheck-interval
大小为 5 分钟,dfs.heartbeat.interval
默认为 3 秒。
<!---->
<property>
<name>dfs.namenode.heartbeat.recheck-interval</name>
<value>300000</value>
<description>
This time decides the interval to check for expired datanodes.
With this value and dfs.heartbeat.interval, the interval of
deciding the datanode is stale or not is also calculated.
The unit of this configuration is millisecond.
</description>
</property>
<!---->
<property>
<name>dfs.heartbeat.interval</name>
<value>3</value>
<description>
Determines datanode heartbeat interval in seconds.
Can use the following suffix (case insensitive):
ms(millis), s(sec), m(min), h(hour), d(day)
to specify the time (such as 2s, 2m, 1h, etc.).
Or provide complete number in seconds (such as 30 for 30 seconds).
If no time unit is specified then seconds is assumed.
</description>
</property>
复制代码
问题
问题一
问题详情
Permission denied: user=dr.who, access=WRITE, inode=“/“:root:supergroup:drwxr-xr-x
复制代码
解决方案
评论