HDFS
HDFS
一、HDFS 优缺点
1.1 HDFS优点
- 高容错性
- 数据自动保存多个副本。它通过增加副本的形式,提高容错性。
- 某一个副本丢失以后,它可以自动恢复。
- 适合处理大数据
- 数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据;
- 文件规模:能够处理百万规模以上的文件数量,数量相当之大。
- 可构建在廉价机器上,通过多副本机制,提高可靠性。
1.2 HDFS缺点
- 不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
- 无法高效的对大量小文件进行存储。
- 存储大量小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的;
- 小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
- 不支持并发写入、文件随机修改。
- 一个文件只能有一个写,不允许多个线程同时写;
- 仅支持数据append(追加),不支持文件的随机修改
二、HDFS 架构
2.1 NameNode
- 管理HDFS的名称空间
- 配置副本策略
- 管理数据块(Block)映射信息
2.2 Secondary NameNode
并非NameNode的热备。当NameNode挂掉的时候,它并不能马上替换NameNode提供服务。
- 辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode
- 在紧急情况下,可辅助恢复NameNode
2.3 Client
- 文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传;
- 与NameNode交互,获取文件的位置信息;
- 与DataNode交互,读取或者写入数据;
- Client提供一些命令来管理HDFS,比如NameNode格式化;
- Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作;
2.4 DataNode
就是Slave。NameNode 下达命令,DataNode执行实际的操作。
- 存储实际的数据块;
- 执行数据块的读/写操作。
2.5 HDFS 写数据流程
- Client:向NameNode请求上传文件xx
- NameNode:响应是否可以上传文件(会检查权限,目录结构等)
- Client:请求上传第一个Block,让NameNode返回DataNode
- NameNode:返回节点(优先本地节点,比如在DN1上传数据,就优先放DN1,NameNode还得考虑负载均衡等)
- Client:请求与DataNode建立Block传输通道
- DataNode:应答
- Client:传输数据
- Client:告诉Namenode数据传输完成
2.6 HDFS 读数据流程
- Client:请求下载文件xx
- NameNode:返回目标文件的元数据
- Client:请求读取DataNode数据(多个DataNode
- DataNode:传输数据
2.7 NameNode工作机制
NameNode
- NameNode:通电后先加载edits_inprogress_xx 和 fsimage_xx到内存
- Client:发送元数据的增删改请求 xxx
- NameNode:在edits_inprogress中记录要干什么,然后再更改内存
- NameNode:内存数据增删改
Secondary NameNode
- Secondary NameNode:问NameNode是否要CheckPoint (触发条件,定时间到(默认一小时)或Edits中数据满了)
- Secondary NameNode:可以CheckPoint以后请求执行
- NameNode:生成新的edits_inprogress,如果CheckPoint过程中有新的访问,记录会往新的edits_inprogress文件写。原来的edits_inprogress变为edits_xx
- Secondary NameNode:把NameNode的fsimage和edits_xx都拷贝过来
- Secondary NameNode:将拷贝的文件加载到内存,然后执行合并操作。
- Secondary NameNode:生成一个新文件
fsimage.chkpoint
- Secondary NameNode:把新文件
fsimage.chkpoint
拷贝到NameNode - NameNode:把
fsimage.chkpoint
修改名称覆盖fsimage
2.8 Fsimage和Edits概念
- Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目 录和文件inode的序列化信息。
- Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先 会被记录到Edits文件中。
- seen_txid文件保存的是一个数字,就是最后一个edits_的数字
- 每次NameNode启动的时候都会将Fsimage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合并。
- oiv 查看 fsimage 文件
[root@master01 current]# pwd
/opt/hadoop-3.3.1/data/dfs/name/current
[root@master01 current]# hdfs oiv -p XML -i fsimage_0000000000000000304 -o ~/fsimage.xml
[root@master01 current]# cat ~/fsimage.xml
2)oev 查看 Edits 文件
[root@master01 current]# pwd
/opt/hadoop-3.3.1/data/dfs/name/current
[root@master01 current]# hdfs oev -p XML -i edits_0000000000000000303-0000000000000000304 -o ~/edits.xml
[root@master01 current]# cat ~/edits.xml
2.9 CheckPoint 时间设置
- 通常情况下,SecondaryNameNode 每隔一小时执行一次。
hdfs-default.xml
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600s</value>
</property>
- 一分钟检查一次操作次数,当操作次数达到 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>
2.10 DataNode工作机制
- 一个数据块在 DataNode 上以文件形式存储在磁盘上,包括两个文件,一个是数据 本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
- DataNode 启动后向 NameNode 注册,通过后,周期性(6 小时)的向 NameNode 上 报所有的块信息。 DN 向 NN 汇报当前解读信息的时间间隔,默认 6 小时;DN 扫描自己节点块信息列表的时间,默认 6 小时
<property> <name>dfs.blockreport.intervalMsec</name> <value>21600000</value> <description>Determines block reporting interval in milliseconds.</description> </property>
<property> <name>dfs.datanode.directoryscan.interval</name> <value>21600s</value> </property>
- 心跳是每 3 秒一次,心跳返回结果带有 NameNode 给该 DataNode 的命令如复制块 数据到另一台机器,或删除某个数据块。如果超过 10 分钟没有收到某个 DataNode 的心跳, 则认为该节点不可用。
- 集群运行中可以安全加入和退出一些机器。
2.11 DataNode 数据完整性
- 当 DataNode 读取 Block 的时候,它会计算 CheckSum。
- 如果计算后的 CheckSum,与 Block 创建时值不一样,说明 Block 已经损坏。
- Client 读取其他 DataNode 上的 Block。
- 常见的校验算法 crc(32),md5(128),sha1(160)
- DataNode 在其文件创建后周期验证 CheckSum。
2.12 DataNode 掉线时间参数
- 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秒。
需要注意的是 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>
三、HDFS 常用 shell 命令
3.1 从本地加载文件到 HDFS
# 二选一执行即可
hadoop fs -put [localsrc] [dst]
hadoop fs -copyFromLocal [localsrc] [dst]
3.2 从 HDFS 导出文件到本地
# 二选一执行即可
hadoop fs -get [dst] [localsrc]
hadoop fs -copyToLocal [dst] [localsrc]
3.3 统计当前目录下各文件大小
- 默认单位字节
- -s : 显示所有文件大小总和,
- -h : 将以更友好的方式显示文件大小(例如 64.0m 而不是 67108864)
hadoop fs -du <path>
# 显示某个目录的总大小
hadoop fs -du -h -s /user/hive/warehouse/name
3.4 合并下载多个文件
- -nl 在每个文件的末尾添加换行符(LF)
- -skip-empty-file 跳过空文件
hadoop fs -getmerge
# 示例 将HDFS上的hbase-policy.xml和hbase-site.xml文件合并后下载到本地的/usr/test.xml
hadoop fs -getmerge -nl /test/hbase-policy.xml /test/hbase-site.xml /usr/test.xml
3.5 更改文件复制因子
hadoop fs -setrep [-R] [-w] <numReplicas> <path>
- 更改文件的复制因子。如果 path 是目录,则更改其下所有文件的复制因子
- -w : 请求命令是否等待复制完成
# 示例
hadoop fs -setrep -w 3 /user/hadoop/dir1
3.6 权限控制
# 权限控制和Linux上使用方式一致
# 变更文件或目录的所属群组。 用户必须是文件的所有者或超级用户。
hadoop fs -chgrp [-R] GROUP URI [URI ...]
# 修改文件或目录的访问权限 用户必须是文件的所有者或超级用户。
hadoop fs -chmod [-R] <MODE[,MODE]... | OCTALMODE> URI [URI ...]
# 修改文件的拥有者 用户必须是超级用户。
hadoop fs -chown [-R] [OWNER][:[GROUP]] URI [URI ]
创建hive目录和权限
hadoop fs –mkdir /tmp
hadoop fs –chmod a+w /tmp
hadoop fs –mkdir /user/hive/warehouse
hadoop fs –chmod a+w /user/hive/warehouse
3.7 追加一个文件到已经存在的文件末尾
hadoop fs -appendToFile aa.txt bb.txt
四、HDFS Java API
4.1 环境准备
- 官方二进制包
tar -xzf Documents/Soft/BigData/Hadoop/hadoop-3.3.1.tar.gz
- 设置环境变量(windows)
export HADOOP_HOME=/Users/haseochen/hadoop-3.3.1
export PATH=$PATH:$HADOOP_HOME/bin
- Maven依赖 开源版
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.30</version>
</dependency>
</dependencies>
- Maven依赖 CHD版
想要使用 HDFS API,需要导入依赖 hadoop-client
。如果是 CDH 版本的 Hadoop,还需要额外指明其仓库地址:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.heibaiying</groupId>
<artifactId>hdfs-java-api</artifactId>
<version>1.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hadoop.version>2.6.0-cdh5.15.2</hadoop.version>
</properties>
<!---配置 CDH 仓库地址-->
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<dependencies>
<!--Hadoop-client-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
4.2 FileSystem
FileSystem 是所有 HDFS 操作的主入口。由于之后的每个单元测试都需要用到它,这里使用 @Before
注解进行标注。
// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java
private static final String HDFS_PATH = "hdfs://192.168.0.106:8020";
private static final String HDFS_USER = "root";
private static FileSystem fileSystem;
@Before
public void prepare() {
try {
Configuration configuration = new Configuration();
// 这里我启动的是单节点的 Hadoop,所以副本系数设置为 1,默认值为 3
configuration.set("dfs.replication", "1");
fileSystem = FileSystem.get(new URI(HDFS_PATH), configuration, HDFS_USER);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
@After
public void destroy() {
fileSystem = null;
}
4.3 创建目录
支持递归创建目录:
// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java
@Test
public void mkDir() throws Exception {
fileSystem.mkdirs(new Path("/hdfs-api/test0/"));
}
4.4 创建指定权限的目录
FsPermission(FsAction u, FsAction g, FsAction o)
的三个参数分别对应:创建者权限,同组其他用户权限,其他用户权限,权限值定义在 FsAction
枚举类中。
@Test
public void mkDirWithPermission() throws Exception {
fileSystem.mkdirs(new Path("/hdfs-api/test1/"),
new FsPermission(FsAction.READ_WRITE, FsAction.READ, FsAction.READ));
}
4.5 创建文件,并写入内容
@Test
public void create() throws Exception {
// 如果文件存在,默认会覆盖, 可以通过第二个参数进行控制。第三个参数可以控制使用缓冲区的大小
FSDataOutputStream out = fileSystem.create(new Path("/hdfs-api/test/a.txt"),
true, 4096);
out.write("hello hadoop!".getBytes());
out.write("hello spark!".getBytes());
out.write("hello flink!".getBytes());
// 强制将缓冲区中内容刷出
out.flush();
out.close();
}
4.6 判断文件是否存在
@Test
public void exist() throws Exception {
boolean exists = fileSystem.exists(new Path("/hdfs-api/test/a.txt"));
System.out.println(exists);
}
4.7 查看文件内容
查看小文本文件的内容,直接转换成字符串后输出:
@Test
public void readToString() throws Exception {
FSDataInputStream inputStream = fileSystem.open(new Path("/hdfs-api/test/a.txt"));
String context = inputStreamToString(inputStream, "utf-8");
System.out.println(context);
}
inputStreamToString
是一个自定义方法,代码如下:
/**
* 把输入流转换为指定编码的字符
*
* @param inputStream 输入流
* @param encode 指定编码类型
*/
private static String inputStreamToString(InputStream inputStream, String encode) {
try {
if (encode == null || ("".equals(encode))) {
encode = "utf-8";
}
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, encode));
StringBuilder builder = new StringBuilder();
String str = "";
while ((str = reader.readLine()) != null) {
builder.append(str).append("\n");
}
return builder.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
4.8 文件重命名
@Test
public void rename() throws Exception {
Path oldPath = new Path("/hdfs-api/test/a.txt");
Path newPath = new Path("/hdfs-api/test/b.txt");
boolean result = fileSystem.rename(oldPath, newPath);
System.out.println(result);
}
4.9 删除目录或文件
public void delete() throws Exception {
/*
* 第二个参数代表是否递归删除
* + 如果 path 是一个目录且递归删除为 true, 则删除该目录及其中所有文件;
* + 如果 path 是一个目录但递归删除为 false,则会则抛出异常。
*/
boolean result = fileSystem.delete(new Path("/hdfs-api/test/b.txt"), true);
System.out.println(result);
}
4.10 上传文件到HDFS
@Test
public void copyFromLocalFile() throws Exception {
// 如果指定的是目录,则会把目录及其中的文件都复制到指定目录下
Path src = new Path("D:\\BigData-Notes\\notes\\installation");
Path dst = new Path("/hdfs-api/test/");
fileSystem.copyFromLocalFile(src, dst);
}
4.11 上传大文件并显示上传进度
// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java
@Test
public void copyFromLocalBigFile() throws Exception {
File file = new File("D:\\kafka.tgz");
final float fileSize = file.length();
InputStream in = new BufferedInputStream(new FileInputStream(file));
FSDataOutputStream out = fileSystem.create(new Path("/hdfs-api/test/kafka5.tgz"),
new Progressable() {
long fileCount = 0;
public void progress() {
fileCount++;
// progress 方法每上传大约 64KB 的数据后就会被调用一次
System.out.println("上传进度:" + (fileCount * 64 * 1024 / fileSize) * 100 + " %");
}
});
IOUtils.copyBytes(in, out, 4096);
}
4.12 从HDFS上下载文件
@Test
public void copyToLocalFile() throws Exception {
Path src = new Path("/hdfs-api/test/kafka.tgz");
Path dst = new Path("D:\\app\\");
/*
* 第一个参数控制下载完成后是否删除源文件,默认是 true,即删除;
* 最后一个参数表示是否将 RawLocalFileSystem 用作本地文件系统;
* RawLocalFileSystem 默认为 false,通常情况下可以不设置,
* 但如果你在执行时候抛出 NullPointerException 异常,则代表你的文件系统与程序可能存在不兼容的情况 (window 下常见),
* 此时可以将 RawLocalFileSystem 设置为 true
*/
fileSystem.copyToLocalFile(false, src, dst, true);
}
4.13 查看指定目录下所有文件的信息
// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java
public void listFiles() throws Exception {
FileStatus[] statuses = fileSystem.listStatus(new Path("/hdfs-api"));
for (FileStatus fileStatus : statuses) {
//fileStatus 的 toString 方法被重写过,直接打印可以看到所有信息
System.out.println(fileStatus.toString());
}
}
FileStatus
中包含了文件的基本信息,比如文件路径,是否是文件夹,修改时间,访问时间,所有者,所属组,文件权限,是否是符号链接等,输出内容示例如下:
FileStatus{
path=hdfs://192.168.0.106:8020/hdfs-api/test;
isDirectory=true;
modification_time=1556680796191;
access_time=0;
owner=root;
group=supergroup;
permission=rwxr-xr-x;
isSymlink=false
}
4.14 递归查看指定目录下所有文件的信息
@Test
public void listFilesRecursive() throws Exception {
RemoteIterator<LocatedFileStatus> files = fileSystem.listFiles(new Path("/hbase"), true);
while (files.hasNext()) {
System.out.println(files.next());
}
}
和上面输出类似,只是多了文本大小,副本系数,块大小信息。
LocatedFileStatus{
path=hdfs://192.168.0.106:8020/hbase/hbase.version;
isDirectory=false;
length=7;
replication=1;
blocksize=134217728;
modification_time=1554129052916;
access_time=1554902661455;
owner=root; group=supergroup;
permission=rw-r--r--;
isSymlink=false}
4.15 查看文件的块信息
@Test
public void getFileBlockLocations() throws Exception {
FileStatus fileStatus = fileSystem.getFileStatus(new Path("/hdfs-api/test/kafka.tgz"));
BlockLocation[] blocks = fileSystem.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());
for (BlockLocation block : blocks) {
System.out.println(block);
}
}
五、HDFS 核心参数
5.1 NameNode 内存生产配置
- NameNode 内存计算
每个文件块大概占用 150byte,一台服务器 128G 内存为例,能存储多少文件块呢?
128 * 1024 * 1024 * 1024 / 150Byte ≈ 9.1亿
G MB KB Byte
- Hadoop2.x 系列,配置 NameNode 内存
NameNode 内存默认 2000m,如果服务器内存 4G,NameNode 内存可以配置 3g。在hadoop-env.sh 文件中配置如下。
HADOOP_NAMENODE_OPTS=-Xmx3072m
- Hadoop3.x 系列,配置 NameNode 内存
hadoop-env.sh 中描述 Hadoop 的内存是动态分配的
具体修改:hadoop-env.sh
export HDFS_NAMENODE_OPTS="-Dhadoop.security.logger=INFO,RFAS - Xmx1024m"
export HDFS_DATANODE_OPTS="-Dhadoop.security.logger=ERROR,RFAS -Xmx1024m"
- 查看 NameNode 占用内存
[tpxcer@master01 root]$ jps
75153 NameNode
75377 DataNode
75741 NodeManager
76015 JobHistoryServer
45775 Jps
[tpxcer@master01 root]$ jmap -heap 75153
Heap Configuration:
MaxHeapSize = 2046820352 (1952.0MB)
[tpxcer@master01 root]$ jmap -heap 75377
Heap Configuration:
MaxHeapSize = 2046820352 (1952.0MB)
上面 NameNode 和 DataNode 占用内存都是自动分配的,且相等。 不是很合理。 经验参考: https://docs.cloudera.com/documentation/enterprise/6/release-notes/topics/rg_hardware_requirements.html
5.2 NameNode 心跳并发配置
hdfs-site.xml
The number of Namenode RPC server threads that listen to requests from clients. If dfs.namenode.servicerpc-address is not configured then Namenode RPC server threads listen to requests from all nodes.
NameNode 有一个工作线程池,用来处理不同 DataNode 的并发心跳以及客户端并发 的元数据操作。
对于大集群或者有大量客户端的集群来说,通常需要增大该参数。默认值是 10。 <property>
<name>dfs.namenode.handler.count</name>
<value>21</value>
</property>
企业经验:dfs.namenode.handler.count=20 × 𝑙𝑜𝑔𝐶𝑙𝑢𝑠𝑡𝑒𝑟 𝑆𝑖𝑧𝑒,比如集群规模(DataNode 台 𝑒 数)为 3 台时,此参数设置为 21。可通过简单的 python 代码计算该值,代码如下。
``Bash
import math print int(20*math.log(3)) 21 quit() ``
5.3 开启回收站配置
开启回收站功能,可以将删除的文件在不超时的情况下,恢复原数据,起到防止误删除、 备份等作用。
- 开启回收站功能参数说明
- 默认值 fs.trash.interval = 0,0 表示禁用回收站;其他值表示设置文件的存活时间。
- 默认值 fs.trash.checkpoint.interval = 0,检查回收站的间隔时间。如果该值为 0,则该值设置和 fs.trash.interval 的参数值相等。
- 要求 fs.trash.checkpoint.interval <= fs.trash.interval。
- 启用回收站
修改 core-site.xml,配置垃圾回收时间为 1 分钟。
<property>
<name>fs.trash.interval</name>
<value>1</value>
</property>
- 查看回收站
/user/xxx/.Trash/...
- 通过网页上直接删除的文件也不会走回收站。
- 通过程序删除的文件不会经过回收站,需要调用 moveToTrash()才进入回收站
Trash trash = New Trash(conf);
trash.moveToTrash(path);
- 只有在命令行利用 hadoop fs -rm 命令删除的文件才会走回收站。
5.4 NameNode 多目录配置
在 hdfs-site.xml 文件中添加如下内容
<property>
<name>dfs.namenode.name.dir</name>
<value>file://${hadoop.tmp.dir}/dfs/name1,file://${hadoop.tmp.dir}/dfs/name2</value>
</property>
检查 name1 和 name2 里面的内容,发现一模一样。
5.4 DataNode 多目录配置
<property>
<name>dfs.datanode.data.dir</name>
<value>file://${hadoop.tmp.dir}/dfs/data1,file://${hadoop.tmp. dir}/dfs/data2</value>
</property>
5.5 集群数据均衡之磁盘间数据均衡
生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x 新特性)
- 生成均衡计划(我们只有一块磁盘,不会生成计划)
hdfs diskbalancer -plan hadoop103
- 执行均衡计划
hdfs diskbalancer -execute hadoop103.plan.json
- 查看当前均衡任务的执行情况
hdfs diskbalancer -query hadoop103
- 取消均衡任务
hdfs diskbalancer -cancel hadoop103.plan.json
六、HDFS 群集压测
6.1 测试 HDFS 写性能
- 测试内容:向 HDFS 集群写 10 个 128M 的文件
[tpxcer@master01 root]$ hadoop jar /opt/hadoop-3.3.1/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.3.1-tests.jar TestDFSIO -write -nrFiles 15 -fileSize 128MB
2021-09-03 17:52:48,167 INFO fs.TestDFSIO: ----- TestDFSIO ----- : write
2021-09-03 17:52:48,167 INFO fs.TestDFSIO: Date & time: Fri Sep 03 17:52:48 CST 2021
2021-09-03 17:52:48,167 INFO fs.TestDFSIO: Number of files: 15
2021-09-03 17:52:48,167 INFO fs.TestDFSIO: Total MBytes processed: 1920
2021-09-03 17:52:48,167 INFO fs.TestDFSIO: Throughput mb/sec: 1.2
2021-09-03 17:52:48,168 INFO fs.TestDFSIO: Average IO rate mb/sec: 2.7
2021-09-03 17:52:48,168 INFO fs.TestDFSIO: IO rate std deviation: 5.7
2021-09-03 17:52:48,168 INFO fs.TestDFSIO: Test exec time sec: 161.51
nrFiles n为生成mapTask的数量,生产环境一般可通过hadoop103:8088查看CPU 核数,设置为(CPU 核数 - 1)
- Numberoffiles:生成mapTask数量,一般是集群中(CPU核数-1)
- TotalMBytesprocessed:单个map处理的文件大小
- Throughputmb/sec:单个mapTak的吞吐量
计算方式:处理的总文件大小/每一个 mapTask 写数据的时间累加 集群整体吞吐量:生成 mapTask 数量*单个 mapTak 的吞吐量 - Average IO rate mb/sec::平均 mapTak 的吞吐量 计算方式:每个 mapTask 处理文件大小/每一个 mapTask 写数据的时间全部相加除以 task 数量
- IO rate std deviation:方差、反映各个 mapTask 处理的差值,越小越均衡
- 测试结果分析
一共参与测试的文件:15 个文件 * 3 个副本 = 45 个 压测后的速度:1.2 实测速度:1.2M/s * 45 个文件 ≈ 54M/s
6.2 测试 HDFS 读性能
测试内容:读取 HDFS 集群 10 个 128M 的文件
[tpxcer@master01 root]$ hadoop jar /opt/hadoop-3.3.1/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.3.1-tests.jar TestDFSIO -read -nrFiles 8 -fileSize 128MB
2021-09-03 18:22:56,694 INFO fs.TestDFSIO: ----- TestDFSIO ----- : read
2021-09-03 18:22:56,695 INFO fs.TestDFSIO: Date & time: Fri Sep 03 18:22:56 CST 2021
2021-09-03 18:22:56,695 INFO fs.TestDFSIO: Number of files: 8
2021-09-03 18:22:56,695 INFO fs.TestDFSIO: Total MBytes processed: 1024
2021-09-03 18:22:56,695 INFO fs.TestDFSIO: Throughput mb/sec: 130.05
2021-09-03 18:22:56,695 INFO fs.TestDFSIO: Average IO rate mb/sec: 131.33
2021-09-03 18:22:56,695 INFO fs.TestDFSIO: IO rate std deviation: 12.87
2021-09-03 18:22:56,695 INFO fs.TestDFSIO: Test exec time sec: 43.76
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:
6.3 删除测试生成数据
[tpxcer@master01 root]$ hadoop jar /opt/hadoop-3.3.1/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.3.1-tests.jar TestDFSIO -clean
七、HDFS 集群扩容及缩容
7.1 添加白名单
在白名单中的主机ip都可以随意访问群集数据
- 在
/hadoop-3.3.1/etc/hadoop
目录下分别创建whitelist
和blacklist
文件 - 在白名单中添加主机名
master01
slave01
slave02
- 在
hdfs-site.xml
配置文件中增加dfs.hosts配置参数
<!-- 白名单 -->
<property>
<name>dfs.hosts</name>
<value>/hadoop-3.3.1/etc/hadoop/whitelist</value>
</property>
<!-- 黑名单 -->
<property>
<name>dfs.hosts.exclude</name>
<value>/hadoop-3.3.1/etc/hadoop/blacklist</value>
</property>
- 分发配置文件
- 第一次添加白名单必须重启集群,不是第一次,只需要刷新 NameNode 节点即可
# 刷新节点
hdfs dfsadmin -refreshNodes
7.2 服役新服务器
- 环境准备
- 克隆一台Hadoop主机
- 修改IP地址和主机名称
- 拷贝配置
- 删除 克隆 Hadoop 的历史数据,data 和 log 数据
- 配置ssh 无密登录
- 直接启动 DataNode,即可关联到集群
- 在白名单中增加新服役的服务器
7.3 服务器间数据均衡
- 开启数据均衡命令
sbin/start-balancer.sh - threshold 10
对于参数 10,代表的是集群中各个节点的磁盘空间利用率相差不超过 10%,可根据实际情况进行调整。
- 停止数据均衡命令
sbin/stop-balancer.sh
由于 HDFS 需要启动单独的 Rebalance Server 来执行 Rebalance 操作,所以尽量 不要在 NameNode 上执行 start-balancer.sh,而是找一台比较空闲的机器。
7.4 黑名单退役服务器
- 第一次添加黑名单必须重启集群,不是第一次,只需要刷新 NameNode 节点即可
hdfs dfsadmin -refreshNodes Refresh nodes successful
- 检查 Web 浏览器,退役节点的状态为 decommission in progress(退役中),说明数据 节点正在复制块到其他节点
- 等待退役节点状态为 decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。
hdfs --daemon stop datanode
yarn --daemon stop nodemanager
注意:如果副本数是 3,服役的节点小于等于 3,是不能退役成功的,需要修改 副本数后才能退役
- 如果数据不均衡,可以用命令实现集群的再平衡
sbin/start-balancer.sh - threshold 10
八、HDFS 存储优化
8.1 纠删码
HDFS 默认情况下,一个文件有 3 个副本,这样提高了数据的可靠性,但也带来了 2 倍 的冗余开销。Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约 50%左右的存储空间。
- 纠删码操作相关的命令
[tpxcer@master01 ~]$ hdfs ec
- 查看当前支持的纠删码策略
[tpxcer@master01 ~]$ hdfs ec -listPolicies
Erasure Coding Policies:
ErasureCodingPolicy=[Name=RS-10-4-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=10, numParityUnits=4]], CellSize=1048576, Id=5], State=DISABLED
ErasureCodingPolicy=[Name=RS-3-2-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=3, numParityUnits=2]], CellSize=1048576, Id=2], State=DISABLED
ErasureCodingPolicy=[Name=RS-6-3-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=1], State=ENABLED
ErasureCodingPolicy=[Name=RS-LEGACY-6-3-1024k, Schema=[ECSchema=[Codec=rs-legacy, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=3], State=DISABLED
ErasureCodingPolicy=[Name=XOR-2-1-1024k, Schema=[ECSchema=[Codec=xor, numDataUnits=2, numParityUnits=1]], CellSize=1048576, Id=4], State=DISABLED
- 纠删码策略解释
RS-3-2-1024k:使用 RS 编码,每 3 个数据单元,生成 2 个校验单元,共 5 个单元,也就是说:这 5 个单元中,只要有任意的 3 个单元存在(不管是数据单元还是校验单元,只要总数=3),就可以得到原始数据。每个单元的大小是 1024k=1024*1024=1048576。
RS-10-4-1024k:使用 RS 编码,每 10 个数据单元(cell),生成 4 个校验单元,共 14 个单元,也就是说:这 14 个单元中,只要有任意的 10 个单元存在(不管是数据单元还是校 验单元,只要总数=10),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576。
RS-6-3-1024k:使用 RS 编码,每 6 个数据单元,生成 3 个校验单元,共 9 个单元,也 就是说:这 9 个单元中,只要有任意的 6 个单元存在(不管是数据单元还是校验单元,只要 总数=6),就可以得到原始数据。每个单元的大小是 1024k=1024*1024=1048576。
RS-LEGACY-6-3-1024k:策略和上面的 RS-6-3-1024k 一样,只是编码的算法用的是 rs-legacy。
XOR-2-1-1024k:使用 XOR 编码(速度比 RS 编码快),每 2 个数据单元,生成 1 个校 验单元,共 3 个单元,也就是说:这 3 个单元中,只要有任意的 2 个单元存在(不管是数据 单元还是校验单元,只要总数= 2),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576。
8.2 纠删码案例实操
纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k 策略的支持,如要使用别的策略需要提前启用。
需求:将/input 目录设置为 RS-3-2-1024k 策略
- 开启对 RS-3-2-1024k 策略的支持
hdfs ec -enablePolicy -policy RS-3-2-1024k
- 在 HDFS 创建目录,并设置 RS-3-2-1024k 策略
hdfs dfs -mkdir /input
hdfs ec -setPolicy -path /input -policy RS-3-2-1024k
九、异构存储(冷热数据分离)
异构存储主要解决,不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。
9.1 存储类型和存储策略
- 关于存储类型
- RAM_DISK:(内存镜像文件系统)
- SSD:(SSD固态硬盘)
- DISK:(普通磁盘,在HDFS中,如果没有主动声明数据目录存储类型默认都是DISK)
- ARCHIVE:(没有特指哪种存储介质,主要的指的是计算能力比较弱而存储密度比较高的存储介质,用来解决数据量的 容量扩增的问题,一般用于归档)
- 关于存储策略 说明:从Lazy_Persist到Cold,分别代表了设备的访问速度从快到慢
策略ID | 策略名称 | 副本分布 | 说明 |
---|---|---|---|
15 | Lazy_Persist | RAM_DISK:1,DISK:n-1 | 一个副本保存在内存RAM_DISK中,其余副本保存在磁盘中。 |
12 | All_SSD | SSD:n | 所有副本都保存在SSD中。 |
10 | One_SSD | SSD:1,DISK:n-1 | 一个副本保存在SSD中,其余副本保存在磁盘中。 |
7 | Hot(default) | DISK:n | Hot:所有副本保存在磁盘中,这也是默认的存储策略。 |
5 | Warm | DSIK:1,ARCHIVE:n-1 | 一个副本保存在磁盘上,其余副本保存在归档存储上。 |
2 | Cold | ARCHIVE:n | 所有副本都保存在归档存储上。 |
9.2 异构存储 Shell 操作
- 查看当前有哪些存储策略可以用
[tpxcer@master01 ~]$ hdfs storagepolicies -listPolicies
- 为指定路径(数据存储目录)设置指定的存储策略
hdfs storagepolicies -setStoragePolicy -path xxx -policy xxx
- 获取指定路径(数据存储目录或文件)的存储策略
hdfs storagepolicies -getStoragePolicy -path xxx
- 取消存储策略;执行改命令之后该目录或者文件,以其上级的目录为准,如果是根目录,那么就是 HOT
hdfs storagepolicies -unsetStoragePolicy -path xxx
- 查看文件块的分布
bin/hdfs fsck xxx -files -blocks -locations
- 查看集群节点
hadoop dfsadmin -report
9.3 测试环境准备& 实践
待完善
十、HDFS 故障排除
11.1 NameNode 故障处理
NameNode 进程挂了并且存储的数据也丢失了,如何恢复 NameNode
- kill -9 NameNode 进程
kill -9 19886
- 删除 NameNode 存储的数据(/opt/hadoop-x.x.x/data/tmp/dfs/name)
rm -rf /opt/module/hadoop-x.x.x/data/dfs/name/*
- 拷贝 SecondaryNameNode 中数据到原 NameNode 存储数据目录
scp -r xxx@xxx:/opt/module/hadoop-x.x.x/data/dfs/namesecondary/* ./name/
- 重新启动 NameNode
hdfs --daemon start namenode
11.2 集群安全模式
安全模式
:文件系统只接受读数据请求,而不接受删除、修改等变更请求- 进入安全模式场景
- NameNode在加载镜像文件和编辑日志期间处于安全模式;
- NameNode在接收DataNode注册时,处于安全模式
- 退出安全模式条件
dfs.namenode.safemode.min.datanodes:最小可用 datanode 数量,默认 0
dfs.namenode.safemode.threshold-pct:副本数达到最小要求的 block 占系统总 block 数的 百分比,默认 0.999f。(只允许丢一个块)
dfs.namenode.safemode.extension:稳定时间,默认值 30000 毫秒,即 30 秒
- 基本语法 集群处于安全模式,不能执行重要操作(写操作)。集群启动完成后,自动退出安全模式。
# 查看安全模式状态
[tpxcer@master01 hadoop-3.3.1]$ hdfs dfsadmin -safemode get
# 进入安全模式状态
bin/hdfs dfsadmin -safemode enter
# 离开安全模式状态
bin/hdfs dfsadmin -safemode leave
# 等待安全模式状态
bin/hdfs dfsadmin -safemode wait
- 案例 磁盘修复
(1)模拟损坏,分别进入各datanode的/hadoop-x.x.x/data/dfs/data/current/
中删除某 2 个块信息
(2)重新启动集群 查看http://master01:9870/dfshealth.html#tab-overview ,会提示安全模式已经打开,块的数量没达到要求
(3)离开安全模式
hdfs dfsadmin -safemode get
hdfs dfsadmin -safemode leave
- 再查看查看http://master01:9870/dfshealth.html#tab-overview 根据提示处理