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目录)
   ![环境变量]()
    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. 
![在这里插入图片描述]()

    解释:  
    ①客户端通过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.

hdfs副本丢失后怎么恢复 hdfs副本数不足_hadoop

结论:  
    1.集群①-机架A-DN1与本身的节点距离:0  
    2.集群①-机架A-DN1与DN2的节点距离:2  
    3.集群①-机架A-DN1与集群①-机架C-DN3:4  
    4.集群①-机架A-DN1与集群②-机架B-DN2:6