一. HDFS概述
1. HDFS组成架构
-
NameNode(nn):就是Master,它 是一个主管、管理者。
NameNode是HDFS中的主节点,负责管理整个文件系统的命名空间和元数据。它记录了文件和数据块的映射关系,并维护了文件系统的目录结构。NameNode还负责处理客户端的读写请求,指导数据节点进行数据的读写操作。由于NameNode存储了整个文件系统的元数据,因此它需要足够的内存和计算资源来支持大规模数据集。
-
DateNode(dn):就是Slave。NameNode 下达命令,DataNode执行实际的操作。
DataNode是HDFS中的工作节点,负责存储实际的数据块。每个数据节点上都会存储一部分数据块,并定期向NameNode发送心跳信号,报告自己的存活状态和数据块的信息。DataNode主要执行数据的读写操作,根据NameNode的指导,将数据块复制到其他数据节点上以实现数据的冗余和容错性。
-
Client:就是客户端。
客户端是与HDFS交互的应用程序或用户界面。客户端通过与NameNode通信来获取文件的元数据信息,包括文件的大小、分块信息等。然后客户端可以直接与数据节点交互来读取或写入数据。客户端还可以通过HDFS提供的API来进行文件操作,例如创建、删除、移动和重命名文件等。
-
Secondary NameNode:并非NameNode的热备。当NameNode挂掉的时候,它并不 能马上替换NameNode并提供服务。
Secondary NameNode并不是NameNode的备份,它是一个辅助性的节点,用于帮助NameNode管理元数据。Secondary NameNode定期从NameNode获取元数据的快照,并将其保存在本地磁盘上。它还可以协助NameNode进行元数据的合并和清理工作,以提高系统的性能和稳定性。
2. HDFS文件块大小
寻址时间除以1%(即0.01)得到传输时间,然后传输时间乘100MB/s所得结果即为块大小。但是在计算机领域,1024才是整数,所以看求出的块大小接近128MB还是256MB,接近哪个,块大小就设置成哪个。
为什么块的大小不能设置太小,也不能设置太大?
- HDFS的块设置太小,会增加寻址时间,程序一直在找块的开始位置。
- 如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开 始位置所需的时间。导致程序在处理这块数据时,会非常慢。
总结::HDFS块的大小设置主要取决于磁盘传输速率(即磁盘读写速度)。在企业开发中,常见的为128MB或256MB,一般大公司用256MB。中小公司用128MB。
二. HDFS的操作
HDFS的操作分为Shell操作和API操作,当然也包括在网页上直接操作。
HDFS的Shell操作
基本语法:
方式一:hadoop fs 具体命令
方式二:hdfs dfs 具体命令
方式一和方式二的效果是完全相同的,且命令的前面都有一个杠-
。
命令大全:
输入hadoop fs
就能看见所以的命令:
[root@hadoop102 ~]# hadoop fs
Usage: hadoop fs [generic options]
[-appendToFile [-n] <localsrc> ... <dst>]
[-cat [-ignoreCrc] <src> ...]
[-checksum [-v] <src> ...]
[-chgrp [-R] GROUP PATH...]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-concat <target path> <src path> <src path> ...]
[-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] [-q <thread pool queue size>] <localsrc> ... <dst>]
[-copyToLocal [-f] [-p] [-crc] [-ignoreCrc] [-t <thread count>] [-q <thread pool queue size>] <src> ... <localdst>]
[-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] [-s] <path> ...]
[-cp [-f] [-p | -p[topax]] [-d] [-t <thread count>] [-q <thread pool queue size>] <src> ... <dst>]
[-createSnapshot <snapshotDir> [<snapshotName>]]
[-deleteSnapshot <snapshotDir> <snapshotName>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] [-v] [-x] <path> ...]
[-expunge [-immediate] [-fs <path>]]
[-find <path> ... <expression> ...]
[-get [-f] [-p] [-crc] [-ignoreCrc] [-t <thread count>] [-q <thread pool queue size>] <src> ... <localdst>]
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-getmerge [-nl] [-skip-empty-file] <src> <localdst>]
[-head <file>]
[-help [cmd ...]]
[-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [<path> ...]]
[-mkdir [-p] <path> ...]
[-moveFromLocal [-f] [-p] [-l] [-d] <localsrc> ... <dst>]
[-moveToLocal <src> <localdst>]
[-mv <src> ... <dst>]
[-put [-f] [-p] [-l] [-d] [-t <thread count>] [-q <thread pool queue size>] <localsrc> ... <dst>]
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ...]
[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
[-setfattr {-n name [-v value] | -x name} <path>]
[-setrep [-R] [-w] <rep> <path> ...]
[-stat [format] <path> ...]
[-tail [-f] [-s <sleep interval>] <file>]
[-test -[defswrz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touch [-a] [-m] [-t TIMESTAMP (yyyyMMdd:HHmmss) ] [-c] <path> ...]
[-touchz <path> ...]
[-truncate [-w] <length> <path> ...]
[-usage [cmd ...]]
Generic options supported are:
-conf <configuration file> specify an application configuration file
-D <property=value> define a value for a given property
-fs <file:///|hdfs://namenode:port> specify default filesystem URL to use, overrides 'fs.defaultFS' property from configurations.
-jt <local|resourcemanager:port> specify a ResourceManager
-files <file1,...> specify a comma-separated list of files to be copied to the map reduce cluster
-libjars <jar1,...> specify a comma-separated list of jar files to be included in the classpath
-archives <archive1,...> specify a comma-separated list of archives to be unarchived on the compute machines
The general command line syntax is:
command [genericOptions] [commandOptions]
此外,还可以通过-help来获得某个命令的具体用法:
-help:输出这个命令参数
[root@hadoop102 ~]# hadoop fs -help setrep
-setrep [-R] [-w] <rep> <path> ... :
Set the replication level of a file. If <path> is a directory then the command
recursively changes the replication factor of all files under the directory tree
rooted at <path>. The EC files will be ignored here.
-w It requests that the command waits for the replication to complete. This
can potentially take a very long time.
-R It is accepted for backwards compatibility. It has no effect.
1. 上传
-moveFromLocal:从本地剪切粘贴到HDFS
hadoop fs -moveFromLocal ./shuguo.txt /sanguo
-copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去
hadoop fs -copyFromLocal weiguo.txt /sanguo
-put:等同于copyFromLocal,生产环境更习惯用put
hadoop fs -put ./wuguo.txt /sanguo
-appendToFile:追加一个文件到已经存在的文件末尾
hadoop fs -appendToFile liubei.txt /sanguo/shuguo.txt
2. 下载
-copyToLocal:从HDFS拷贝到本地
hadoop fs -copyToLocal /sanguo/shuguo.txt ./
-get:等同于copyToLocal,生产环境更习惯用get
hadoop fs -get /sanguo/shuguo.txt ./shuguo2.txt
3. HDFS直接操作
与Linux命令非常相像,若命令后面还要跟上参数的话,则必须空一格再加参数!!千万不能像Linux一样将两个参数合在一起写!!!
如:rm
后可接两个参数-r
和-f
,在Linux中我们可以写rm -rf
,但是在Shell中我们不能这样写hadoop fs -rm -rf
,得写成hadoop fs -rm -r -f
。
[root@hadoop102 ~]# hadoop fs -rm -r -f /xiyou
Deleted /xiyou
当然,-f
表示强制删除,不需要询问我们。而hdfs不会问我们,所以敲hadoop fs -rm -r
就行了,只不过敲-f
也不会报错啦。
-ls: 显示目录信息
hadoop fs -ls /sanguo
-cat:显示文件内容
hadoop fs -cat /sanguo/shuguo.txt
-chgrp、-chmod、-chown:Linux文件系统中的用法一样,修改文件所属权限
hadoop fs -chgrp root /sanguo/shuguo.txt
hadoop fs -chmod 666 /sanguo/shuguo.txt
hadoop fs -chown root:root /sanguo/shuguo.txt
-mkdir:创建路径
hadoop fs -mkdir /jinguo
-cp:从HDFS的一个路径拷贝到HDFS的另一个路径
hadoop fs -cp /sanguo/shuguo.txt /jinguo
-mv:在HDFS目录中移动文件
hadoop fs -mv /sanguo/weiguo.txt /jinguo
hadoop fs -mv /Lecture1.pdf /guoting.pdf
注:当目的地是目录时,就是单纯的移动文件。若目的地是文件,则相当于剪切过去再重命名。没错,给文件或目录重命名的实质就是剪切并重命名,只不过路径没有发生变化只更改名字罢了。
-tail:显示一个文件的末尾1kb的数据
hadoop fs -tail /jinguo/shuguo.txt
-rm:删除文件或文件夹
hadoop fs -rm /sanguo/shuguo.txt
-rm -r:递归删除目录及目录里面内容
hadoop fs -rm -r /sanguo
-du:统计文件夹的大小信息
hadoop fs -du /jinguo
当然,我们一般使用命令-du
会加上-h
,这样文件大小更容易读。此外,更常用的命令是hadoop fs -du -s -h
,只显示总和,和Linux一样。只不过需要注意别把-s -h
写成-sh
就行。
-setrep:设置HDFS中文件的副本数量
hadoop fs -setrep 10 /jinguo/shuguo.txt
注意:这里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10。
HDFS的API操作
1. 客户端环境准备
配置依赖:
首先需要下载与hadoop同版本的Windows依赖文件,并拷贝到非中文路径。
然后配置系统HADOOP_HOME环境变量。
接着配置Path环境变量。(注意:如果环境变量不起作用,可以重启电脑试试。)
最后验证Hadoop环境变量是否正常:双击winutils.exe,若出现一个一闪而过的窗口则说明正常。
配置Maven:
可以按照一位大神写的教程(传送门)一步步配。只需要注意正确输入自己的hadoop和jdk版本即可,除非版本一模一样,否则千万别直接无脑复制粘贴!
2. IDEA操作
新建工程HDFSClient:
点击new project
,输入名字HDFSClient
,将Build system
选择为Maven
,最后修改Advanced Settings
中的Groupld
即可(这个选项相当于设置包名,一般采用公司域名的倒置作为名字)。
修改IDEA中的Maven配置:
点开设置,搜索"maven",然后点击"Maven"(直接点,别将它展开),再将系统自带的Maven其修改为自己配置的,需要修改的地方有三处,按照自己的Maven进行相应的设置即可。
然后点击设置中目前所在的"Maven"下面的"Importing",找到"VM options for importer",输入下面这行话:
-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true
这样设置是:忽略SSL证书验证,防止连不到阿里云的服务器无法自动下载依赖,而出现找不到依赖的错误 。
导入相应的依赖坐标+日志添加:
创建完Maven工程后会自动生成一些代码,在其中的</properties>
与</project>
之间添加以下内容(注意自己的hadoop版本):
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.5</version>
</dependency>
</dependencies>
因为是首次添加,所以需要让idea联网下载一些文件:点击右边侧边栏的Maven
,然后点击页面顶端的刷新图标,之后耐心等待即可。
新建文件log4j.properties:
在项目的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
创建包com.用户名.hdfs并创建Java类HdfsClient:
之后就可以在这个类中调用API控制hdfs了。
package com.用户名.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
/**
* 客户端代码常用套路:
* 1. 获取一个客户端对象
* 2. 执行相关的操作命令
* 3. 关闭资源
*/
public class HdfsClient {
private FileSystem fs;
@Before
public void init() throws IOException, InterruptedException, URISyntaxException {
// 连接集群的nn地址
URI uri = new URI("hdfs://hadoop102:8020");
// 创建一个配置文件
Configuration configuration = new Configuration();
configuration.set("dfs.replication", "2"); // 修改副本数
// 用户
String user = "root";
// 1. 获取到了客户端对象
fs = FileSystem.get(uri, configuration, user);
}
@After
public void close() throws IOException {
// 3. 关闭资源
fs.close();
}
@Test
public void testMkdir() throws URISyntaxException, IOException, InterruptedException {
// 2. 创建一个文件夹
fs.mkdirs(new Path("/xiyou/huaguoshang"));
}
/**
* 参数优先级:
* hdfs-default.xml -> hdfs-site.xml -> 在项目资源目录下的配置文件 -> 代码里面的配置
*
* @throws IOException
*/
@Test
public void testPut() throws IOException {
fs.copyFromLocalFile(false, true, new Path("D:\\testapi\\sunwukong.txt"), new Path("/xiyou/huaguoshang/"));
}
@Test
public void testRm() throws IOException {
fs.delete(new Path("/xiyou/huaguoshang/sunwukong.txt"), false);
}
@Test
public void testMv() throws IOException {
// 移动
fs.rename(new Path("/sunwukong.txt"), new Path("/xiyou/huaguoshang/sunwukong.txt"));
// 重命名
//fs.rename(new Path("/xiyou/huaguoshang/sunwukong.txt"), new Path("/xiyou/huaguoshang/wukong.txt"));
}
@Test
public void testGet() throws IOException {
fs.copyToLocalFile(false, new Path("/xiyou/huaguoshang"), new Path("D:\\testapi"), false);
}
@Test
public void testListFiles() throws IOException {
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);
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(fileStatus.getAccessTime());
System.out.println(fileStatus.getReplication());
System.out.println(fileStatus.getBlockSize());
System.out.println(fileStatus.getPath().getName());
// 获取块信息
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
System.out.println(Arrays.toString(blockLocations));
}
}
@Test
public void testFiles() throws IOException {
FileStatus[] fileStatuses = fs.listStatus(new Path("/"));
for (FileStatus fileStatus: fileStatuses) {
if (fileStatus.isFile()) System.out.println("文件:" + fileStatus.getPath().getName());
else System.out.println("文件目录:" + fileStatus.getPath().getName());
}
}
}
客户端代码常用套路:
- 获取一个客户端对象
- 执行相关的操作命令
- 关闭资源
参数优先级:
hdfs-default.xml -> hdfs-site.xml -> 在项目资源目录下的配置文件 -> 代码里面的配置