HDFS
1. HDFS定义
1. 说明:一台电脑存不下海量数据,就必须分配到多台机器上存储。
多台机器不方便管理和维护,就需要一种系统来管理多台机器的文件---分布式系统。
2. 使用场景:适合一次写入,多次读取的场景。
2. HDFS优缺点
1. 优点
1. 高容错性
数据自动保存为多副本形式,它通过增加副本的形式,提高容错性。
某个副本丢失后,它可以自动恢复
2. 适合处理大数据。
1. 数据规模:能够处理数据规模达到GB、TB、PB级别的数据。
2. 文件规模:能够处理百万规模以上的文件数量。
3. 可构建在廉价的机器上,通过多副本机制,提高可靠性。(便宜....)
2. 缺点
1. 不适合低延时数据访问——毫秒级的存储数据,是做不到的。
2. 无法高效的对大量小文件进行存储。
存储大量小文件时,会占用NameNode的内存来存储文件信息和块信息,因为NameNode的内存是有限的。所以不好。
小文件的存储的寻址时间会超过读取时间,违反了 HDFS的设计目标。
3. 不支持并发写入和文件修改,只支持追加(append),并且不允许多线程的写入。
3. HDFS架构
1. NameNode(nn):管理者
1. 管理HDFS的名称空间、配置副本策略、管理数据块(Block)映射信息、处理客户端读写请求。
2. DataNode:执行者(执行管理者的命令)
1. 存储实际的数据块、执行数据块的读/写操作。
3. Secondary NameNode:当NameNode挂掉时,并不能马上替换NameNode并提供服务
1. 辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode。
2. 在紧急情况下,可辅助恢复NameNode。
4. Client:客户端
1. 文件切分——上传HDFS时,客户端将文件切分成一个一个的数据块,然后上传。
2. 与NameNode、DataNode交互。
3. 提供命令管理和访问HDFS。
4. HDFS文件块大小(面试重点☆)
1. Block在Hadoop2.x/3.x中默认大小为128MB,在Hadoop1.x中为64MB。
2. 寻址时间为传输时间的1%时,为最佳状态。(☆)
3. 思考:为什么块的大小不能设置太小或太大。
1. 太小,会增加寻址时间。
2. 太大,从磁盘传输数据时间就会大于定位这个块开始位置所需的时间。
总结:HDFS块的大小设置取决于磁盘传输速率。
5. HDFS的Shell操作(开发重点☆)
1. 语法——hadoop fs 或 hdfs dfs:帮助命令 hadoop fs -help rm
2. hadoop fs常用命令:
剪切:hadoop fs -moveFromLocal computer.txt(文件名字) /huade(HDFS上的文件目录)
拷贝:hadoop fs -copyFromLocal bigdata.txt /huade
上传:hadoop fs -put bigdata.txt /huade(生产环境惯用)
下载:hadoop fs -get /huade/computer.txt ./com.txt(生产环境惯用)
追加:hadoop fs -appendToFile a.txt /huade/b.txt(追加到文件末尾)
hadoop fs -ls /huade(显示目录信息)
hadoop fs -cat /huade/computer.txt(显示文件内容)
hadoop fs -chmod 777 /huade/computer.txt(修改文件权限)
hadoop fs -chown test:test /huade/computer.txt(修改文件权限)
hadoop fs -mkdir /xueyuan(创建目录)
hadoop fs -cp /huade/computer.txt /xueyuan(复制文件到另一个路径)
hadoop fs -mv /huade/computer.txt /xueyuan(剪切文件到另一个路径)
hadoop fs -tail /huade/computer.txt(显示一个文件的末尾1kb数据)
hadoop fs -rm /huade/computer.txt(删除一个文件)
hadoop fs -rm -r /huade(递归删除目录及目录里的内容)
hadoop fs -du -s -h /huade(统计文件夹大小信息)
hadoop fs -du -h /huade(统计文件夹大小信息)
hadoop fs -setrep 10 /huade/computer.txt (设置副本的数量)
6. HDFS的API操作
1. HDAOOP环境部署
说明:我们需要在Windows系统里部署Hadoop依赖 ☆
步骤:
1.把Windows-hadoop-3.1.0.zip文件解压至非中文目录 ☆(路径就是由英文、数字、下划线组成,很熟悉的目录)
2.把该路径配置成环境变量:(去找bin目录)
![环境变量](https://img-blog.csdnimg.cn/ff3837d6c285451797e6c0710066153b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YyX5YeJ5bGx5riQ6Z2S,size_18,color_FFFFFF,t_70,g_se,x_16#pic_center)
3.验证Hadoop环境:双击D:\software\Windows-hadoop-3.1.0\bin\winutils.exe ☆
如果一闪而过,说明没问题 ☆
2. Maven工程部署
1. 含义:Maven就是一个项目管理工具(个人开发时,用不到)
2. 作用:解决jar包管理难的问题
jar包之间有依赖问题,有依赖关系,就必然存在一种情况,比如导一个包,就需要导很多包。(jar包还有版本之分)
Maven中管理jar包会自动下载部署
3. 仓库分类
1. 中央仓库:全世界的jar包,都会存进中央仓库
2. 地方仓库:国内大厂的镜像(阿里云)
3. 私服仓库:公司内部的服务器
4. 本地仓库:在本机上,部署Maven仓库。
3.在IDEA中创建一个Maven工程HdfsClientDemo,并导入相应的依赖坐标+日志添加
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</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>
4.在项目的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
执行程序
客户端去操作HDFS时,是有一个用户身份的。默认情况下,HDFS客户端API会从采用Windows默认用户访问HDFS,会报权限异常错误。所以在访问HDFS时,一定要配置用户。
org.apache.hadoop.security.AccessControlException: Permission denied: user=56576, access=WRITE, inode="/xiyou/huaguoshan":atguigu:supergroup:drwxr-xr-x
3. 在IDEA上操作HDFS
package com.huade;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.Test;
import java.net.URI;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;
public class HdfsClient {
// 单元测试
@Test
public void testMkdirs() throws URISyntaxException, IOException,InterruptedException { // 需求: 利用代码在服务器集群中创建目录
// 获取文件系统对象
Configuration configuration= new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"),configuration,"test");
// 创建目录
fs.mkdirs(new Path("/user/demo"));
// 关闭资源
fs.close();
}
// 文件上传
@Test
public void testCopyFileFromLocalFile() throws URISyntaxException, IOException, InterruptedException {
// 获取文件系统对象
Configuration configuration= new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"),configuration,"test");
// 上传文件
Path LocalPath = new Path("D://CopyFile.txt"); // 本地文件地址
Path ClusterPath = new Path("/user/demo"); // 集群地址
fs.copyFromLocalFile(LocalPath,ClusterPath);
// 关闭资源
fs.close();
}
// 文件下载
@Test
public void testCopyFileToLocalFile() throws IOException, URISyntaxException, InterruptedException {
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"),new Configuration(),"test");
fs.copyToLocalFile(false,new Path("/user/demo/CopyFile.txt"),new Path("D://world.txt"),true);
// false是否删除集群文件
// true是否开启文件校验(权限,目录或文件)
// 先写集群后写本地
// 关闭资源
fs.close();
}
// 文件更名
@Test
public void testReNameFile() throws URISyntaxException, IOException, InterruptedException {
// 获取文件系统对象
Configuration configuration= new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"),configuration,"test");
// res为true/false确认是否更名
boolean res = fs.rename(new Path("/user/demo/CopyFile2.txt"),new Path("/user/demo/CopyFileTwo.txt"));
System.out.println(res);
// 关闭资源
fs.close();
}
// 文件移动
@Test
public void testMoveFile() throws URISyntaxException, IOException, InterruptedException {
// 获取文件系统对象
Configuration configuration= new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"),configuration,"test");
// res为true/false确认是否移动
boolean res = fs.rename(new Path("/user/demo/CopyFile2.txt"),new Path("/huade/CopyFileTwo.txt"));
System.out.println(res);
// 关闭资源
fs.close();
}
// 文件删除
@Test
public void testDeLetFile() throws URISyntaxException, IOException, InterruptedException {
// 获取文件系统对象
Configuration configuration= new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"),configuration,"test");
// res为true/false确认是否删除
boolean res = fs.delete(new Path("/user/demo/CopyFile.txt"),true);
System.out.println(res?"删除成功":"删除失败");
// 关闭资源
fs.close();
}
// 查看文件详情
@Test
public void testListFile() throws URISyntaxException, IOException, InterruptedException {
// 获取文件系统对象
Configuration configuration= new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"),configuration,"test");
// 可迭代的对象lf(迭代器)
RemoteIterator<LocatedFileStatus> lf = fs.listFiles(new Path("/user/demo"),true);// 查看全部文件
while(lf.hasNext()){ // 判断是否有下一个
LocatedFileStatus fileStatus = lf.next();
// 获取文件对象后,可以使用对象调用各种方法,获取文件信息
System.out.println(fileStatus.getPermission()); // 文件权限
System.out.println(fileStatus.getOwner()); // 文件所有者
System.out.println(fileStatus.getGroup()); // 文件所有组
System.out.println(fileStatus.getLen()); // 文件大小
System.out.println(fileStatus.getModificationTime()); // 文件修改时间
System.out.println(fileStatus.getReplication()); // 文件副本
System.out.println(fileStatus.getBlockLocations()); // 文件块大小(128)
System.out.println(fileStatus.getPath()); // 文件路径
System.out.println(fileStatus.getPath().getName()); // 文件名称
// 文件块信息
BlockLocation[] arr = fileStatus.getBlockLocations(); // 存放服务器位置
System.out.println(Arrays.toString(arr));
System.out.println("------------------------------");
}
fs.close();
}
// 判断文件或目录
@Test
public void testListStatus() throws URISyntaxException, IOException, InterruptedException {
// 获取文件系统对象
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:8020"), configuration, "test");
FileStatus[] fileStatuses = fs.listStatus(new Path("/user"));
for (FileStatus status : fileStatuses) {
if (status.isFile()) { // 判断是否为文件
System.out.println("文件," + status.getPath().getName());
continue;
}
System.out.println("目录,"+ status.getPath().getName());
}
fs.close();
}
}
7. HDFS写数据流程
1.
![在这里插入图片描述](https://img-blog.csdnimg.cn/ac516a5b625b44978e7b3497e85ba017.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5YyX5YeJ5bGx5riQ6Z2S,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
解释:
①客户端通过Distributed FileSystem模块向NameNode发送上传请求,NameNode检查其权限和目录结构。
②NameNode返回客户端可以上传。
③客户端通过Distributed FileSystem模块再次向NameNode发送具体DN请求,请求第一个Block上传的路径。
④NameNode返回DN节点位置。
⑤客户端创建FSDataOutputStream数据流对象,请求DN1上传数据,DN1接收请求后,继续调用DN2,然后DN2继续调用DN3,通信通道建立完成。
⑥DN1、DN2,DN3逐级应答给客户端。
⑦客户端开始向DN1上传第一个Block块数据,以Packet为单位,DN1收到一个Packet就会传给DN2,DN2传给DN3。当第一个Block块数据传完之后,客户端再次请求NameNode上传第二个Block。(重复3-7步)
2.
结论:
1.集群①-机架A-DN1与本身的节点距离:0
2.集群①-机架A-DN1与DN2的节点距离:2
3.集群①-机架A-DN1与集群①-机架C-DN3:4
4.集群①-机架A-DN1与集群②-机架B-DN2:6