欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HDFS(Hadoop Distribute File System)

程序员文章站 2022-07-10 10:38:52
...

一、基本概念

     一句话概括:HDFS是hadoop分布式文件系统,作用是存储大数据文件,是hadoop领域最基础的部分。

二、HDFS的重要特性

    一群屌丝机组成高富帅

  1、主从架构

    namenode作为master负责管理元数据,datanode作为从节点存储block块数据

    主从:通常是一主多从,主干活,从也干活,负责的分工不同

    主备:通常是一主一备,主要解决的是单节点故障问题,主干活,从为standby

  2、分块存储

    大文件的存储问题:写要能写的而下,读的时候还要读的块,将文件分成block块分布式存储,理论上多大的文件都可以存储,分块后再读取的时候可以并行读取,速度会非常的块。一般hadoop2.x版本的默认是128M

  3、名称空间

    名称空间是指namenode对外有统一的文件路径和命名,形如:hdfs://node01:9000/hadoop32/a.txt,要上传的文件名和要上传的位置,要下载的文件名和下载的文件路径,统一规范(简单理解就是我们通过50070能够看到的文件结构)。

  4、namenode元数据

    namenode负责元数据的管理,(元数据通常可以理解为修饰数据的数据),hdfs中元数据主要指目录结构和文件的块信息

    hdfs的存储规则是尽量不存储小文件,因为一条元数据理论 150byte,例如10W个1Kb的小文件存储的硬盘需要100M,而存储元数据的内存就需要15M。

  5、datanode数据存储

    datanode负责存储block块数据,负责真实数据的存储,启动后需要向namenode进行心跳,并且需要汇报本地块信息。

    datanode主要是存储,也就是需要磁盘,所以多台廉价的屌丝机就可以组成一个强大的HDFS存储集群

  6、副本机制

    副本就是备份,考虑的是容灾的问题,默认每个block有3个副本,他们为相互为对方的副本。

    机架感知(3副本机制)

    HDFS(Hadoop Distribute File System)

  7、一次写入多次读取

  8、限额操作HDFS(Hadoop Distribute File System)

数量限额
hdfs dfsadmin -setQuota 2 限定的文件的路径      # 给该文件夹下面设置最多上传两个文件,上传文件,发现只能上传一个文件
清除权限 -clrQuota

空间限额
假如现在上传的一个文件的备份数是2 现在的空间不足256
假如上传的是129M的文件  那么至少是需要剩余空间512才可以上传文件到hdfs


查看hdfs文件xiane
hdfs dfsadmin -setQuota 2 限定的文件的路径      # 给该文件夹下面设置最多上传两个文件,上传文件,发现只能上传一个文件

三、hdfs常用shell命令介绍

-ls
使用方法:hadoop fs -ls [-h] [-R] <args>
功能:显示文件、目录信息。
示例:hadoop fs -ls /user/hadoop/file1

-mkdir
使用方法:hadoop fs -mkdir [-p] <paths>
功能:在hdfs上创建目录,-p表示会创建路径中的各级父目录。
示例:hadoop fs -mkdir –p /user/hadoop/dir1

-put
使用方法:hadoop fs -put [-f] [-p] [ -|<localsrc1> .. ]. <dst> 
         hadoop fs -put linux中文件的路径  hdfs上的路径
功能:将单个src或多个srcs从本地文件系统复制到目标文件系统。
-p:保留访问和修改时间,所有权和权限。
-f:覆盖目的地(如果已经存在)
示例:hadoop fs -put -f localfile1 localfile2 /user/hadoop/hadoopdir

-get
使用方法:hadoop fs -get [-ignorecrc] [-crc] [-p] [-f] <src> <localdst>
         hadoop fs -get hdfs中文件的路径  本地文件的路径
         hadoop fs -get /hadoop32/word.txt  /export
-ignorecrc:跳过对下载文件的CRC检查。
-crc:为下载的文件写CRC校验和。
功能:将文件复制到本地文件系统(linux上)。
示例:hadoop fs -get hdfs://host:port/user/hadoop/file localfile

-appendToFile 
使用方法:hadoop fs -appendToFile <localsrc> ... <dst>
功能:追加一个文件到已经存在的文件末尾(将文件的内容加载到hdfs中已经存在的文件中)
示例:hadoop fs -appendToFile localfile  /hadoop/hadoopfile
 
-chown
功能:改变文件的拥有者。使用-R将使改变在目录结构下递归进行。
示例:hadoop  fs  -chown  someuser:somegrp   /hadoop/hadoopfile

-cp              
功能:从hdfs的一个路径拷贝hdfs的另一个路径
示例: hadoop  fs  -cp  /aaa/jdk.tar.gz  /bbb/jdk.tar.gz.2

-mv                     
功能:在hdfs目录中移动文件
示例: hadoop  fs  -mv  /aaa/jdk.tar.gz  /

-rm                
功能:删除指定的文件。只删除非空目录和文件。-r 递归删除。
示例:hadoop fs -rm -r /aaa/bbb/

-df               
功能:统计文件系统的可用空间信息
示例:hadoop  fs  -df  -h  /

-du 
功能:显示目录中所有文件大小,当只指定一个文件时,显示此文件的大小。
示例:hadoop fs -du /user/hadoop/dir1

四、HDFS的基本原理

1、NameNode概述

a、	NameNode是HDFS的核心。
b、	NameNode也称为Master。
c、	NameNode仅存储HDFS的元数据:文件系统中所有文件的目录树,并跟踪整个集群中的文件。
d、	NameNode不存储实际数据或数据集。数据本身实际存储在DataNodes中。
e、	NameNode知道HDFS中任何给定文件的块列表及其位置。使用此信息NameNode知道如何从块中构建文件。
f、	NameNode并不持久化存储每个文件中各个块所在的DataNode的位置信息,这些信息会在系统启动时从数据节点重建。
g、	NameNode对于HDFS至关重要,当NameNode关闭时,HDFS / Hadoop集群无法访问。
h、	NameNode是Hadoop集群中的单点故障。
i、	NameNode所在机器通常会配置有大量内存(RAM)。

2、DataNode概述

a、	DataNode负责将实际数据存储在HDFS中。
b、	DataNode也称为Slave。
c、	NameNode和DataNode会保持不断通信。
d、	DataNode启动时,它将自己发布到NameNode并汇报自己负责持有的块列表。
e、	当某个DataNode关闭时,它不会影响数据或群集的可用性。NameNode将安排由其他DataNode管理的块进行副本复制。
f、	DataNode所在机器通常配置有大量的硬盘空间。因为实际数据存储在DataNode中。
g、	DataNode会定期(dfs.heartbeat.interval配置项配置,默认是3秒)向NameNode发送心跳,如果NameNode长时间没有接受到DataNode发送的心跳, NameNode就会认为该DataNode失效。
h、	block汇报时间间隔取参数dfs.blockreport.intervalMsec,参数未配置的话默认为6小时.

3、HDFS的工作机制

  3.1 写过程

HDFS(Hadoop Distribute File System)

1、datanode向namenode汇报自己的状态信息和块列表
2、client找到namenode,向namenode申请上传文件
3、namenode进行校验是否有权限上传,看是否有同名的文件 如果没加入覆盖则不允许上传
4、如果通过,则允许上传文件
5、假如现在的文件是129m 则会对文件进行切割,一个是128 一个是1
6、进行上传 先上传第一个block块,这时会问namenode传送到哪个datanode块上去 ,
7、namenode进行机架感知,选出一个datanode地址列表,并返回给客户端,比如node01、node03
8、这是client就会知道连接哪个datanode。然后建立管道,node01和node03之间也需要建立管道
9、会把block块再次拆分成64k的包然后上传,最后根据偏移量组装成一个新的文件
10、备份成功后会返回一个ack确认码
11、然后继续上传第二个block块

3.2读过程HDFS(Hadoop Distribute File System)

1、申请下载,client向namenode询问 namenode中的namespace保存的是文件的名称和路径以及一些元数据信息
2、这样就会知道你所要下载的文件到底在那个datanode上面 但是有时候备份很多的话,就会根据网络拓扑图翻回最近的一个block块并且是状态好的
3、这样就会得到你需要的文件的地址列表,这样就会连接datanode,进行并行的读取,就会很快速的读取,集群做的够大,他的吞吐能力是非常的强的

五、HDFS的应用操作

1、HDFS的JAVAAPI操作

  1.1搭建开发环境

<repositories>
    <repository>
        <id>cloudera</id>
        <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
    </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-client</artifactId>
        <version>2.6.0-mr1-cdh5.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.6.0-cdh5.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-hdfs</artifactId>
        <version>2.6.0-cdh5.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-mapreduce-client-core</artifactId>
        <version>2.6.0-cdh5.14.0</version>
    </dependency>	
    <!--这里需要在添加一个junit 可以使用4.12版本-->
</dependencies>

1.2stream原始流对接操作--繁琐但灵活

/**
 * @ClassName StreamAccess
 * @Description stream流对接的方式上传下载数据
 */
public class StreamAccess {

    //创建客户端对象
    FileSystem fs = null;

    //创建fs的4种方式
    @Before
    public void createFS() throws IOException, URISyntaxException, InterruptedException {
        Configuration conf = new Configuration();

        //第一种方式 如果什么参数都不给,创建的是本地文件系统 file:///  hdfs  50070 9000
        // 9000是用来操作的 50070是用来看的
        //优先级  当前的配置  > 项目的classpath > 用户设置的hadoop/etc/hadoop/core-site.xml > 默认的core-default.xml
        conf.set("fs.defaultFS","hdfs://node01:9000");
        System.setProperty("HADOOP_USER_NAME","root");
        fs = FileSystem.get(conf);

        //第二种方式(用的最多)
        fs = FileSystem.get(new URI("hdfs://node01:9000"),conf,"root");

        //第三种方式
        conf.set("fs.defaultFS","hdfs://node01:9000");
        fs = FileSystem.newInstance(conf);

        //第四种方式
        fs = FileSystem.newInstance(new URI("hdfs://node01:9000"),conf);
    }

    //上传
    @Test
    public void streamToHdfs() throws Exception {
        //得到本地的输入流
        FileInputStream inputStream = new FileInputStream(new File("d:/pom.xml"));
        //得到hdfs的输出流 要写文件
        FSDataOutputStream outputStream = fs.create(new Path("/hadoop32/1.txt"));
        //流对接
        IOUtils.copy(inputStream,outputStream);
    }

    //下载
    @Test
    public void streamDownLoad() throws Exception {
        //hdfs得到输入流
        FSDataInputStream inputStream = fs.open(new Path("/hadoop32/1.txt"));
        //得到本地文件的输出流
        inputStream.seek(346); // 从哪个位置开始下载
        FileOutputStream outputStream = new FileOutputStream(new File("d:/3.txt"));

        IOUtils.copy(inputStream,outputStream);
    }

    //获取hdfs上的所有文件及路径  hadoop fs -ls -R /
    @Test
    public void findAllFiles() throws Exception {
        RemoteIterator<LocatedFileStatus> iterator = fs.listFiles(new Path("/"), true);// true为是否进行递归查询
        while (iterator.hasNext()) {
            //fileStatus是对文件或文件夹的所属数据信息
            LocatedFileStatus fileStatus = iterator.next();
            System.out.println(fileStatus.getPath());
        }
    }

    //遍历hdfs上所有的文件和文件夹
    @Test
    public void findAll() throws Exception { 
        findFiles("/");
    }

    private void findFiles(String path) throws IOException {
        FileStatus[] fileStatuses = fs.listStatus(new Path(path));
        for (FileStatus fileStatus : fileStatuses) {
            //每一个文件或文件夹的所有数据
            if(fs.isDirectory(fileStatus.getPath())){
                //得到的就是文件夹
                System.out.println("d----"+fileStatus.getPath());
                findFiles(fileStatus.getPath().toString());
            }else{
                //得到的就是文件
                System.out.println("f----"+fileStatus.getPath());
            }
        }
    }

    //获取指定的block块
    @Test
    public void getBlock0() throws Exception {
        FSDataInputStream in = fs.open(new Path("/hadoop32/y.zip"));
        //如果path指定的是一个文件,那么数组返回的大小就是1
        FileStatus[] fileStatuses = fs.listStatus(new Path("/hadoop32/y.zip"));
        FileStatus fileStatus = fileStatuses[0];
        //返回当前文件所有block块的地址列表
        BlockLocation[] fileBlockLocations = fs.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());
       /* for (BlockLocation fileBlockLocation : fileBlockLocations) {
            //得到的是每个块的信息
            System.out.println( fileBlockLocation.getLength()+" offset :"+  fileBlockLocation.getOffset());
            String[] hosts = fileBlockLocation.getHosts();
            //当前块host
            for (String host : hosts) {
                System.out.println("host"+host);
            }
        }*/
        BlockLocation fileBlockLocation = fileBlockLocations[1];
        //第二个block块的偏移量
        long offset = fileBlockLocations[1].getOffset();
        //注意长度的问题:第一个块的长度是128M 第二个块的长度是111M 所以就是从128M向后读了111M的数据,也就是从128M读取到239M的位置,这个长度是指239这个数值
        long length = offset+fileBlockLocations[1].getLength();

        byte[] b = new byte[4096];

        FileOutputStream os = new FileOutputStream(new File("d:/block1"));
        while(in.read(offset, b, 0, 4096)!=-1){
            os.write(b);
            offset += 4096;
            if(offset>length) {
                return;
            }
        };
    }

    @After
    public void close() throws Exception {
        //通常如果是get方式的话不建议关闭,如果是newInstance的话 建议关闭
        fs.close();
    }
}

1.3客户端操作--简单易操作

public class HDFSClient {

    FileSystem fs = null;

    //初始化hdfs文件系统
    @Before
    public void createFS() throws Exception{
        Configuration conf = new Configuration();
        fs = FileSystem.get(new URI("hdfs://node01:9000"),conf,"root");
        //获取到所有datanode节点信息
        DatanodeInfo[] dataNodeStats = ((DistributedFileSystem) fs).getDataNodeStats();
        for (DatanodeInfo dataNodeStat : dataNodeStats) {
            System.out.println(dataNodeStat.getName());
        }
    }

    //文件的上传
    @Test
    public void copyFromLocalFile() throws Exception{
        //文件上传  参数1:是否会删除本地原文件 参数2:是否会覆盖掉hdfs上目标的文件 参数3:本地文件路径  参数4:目标hdfs文件路径
        fs.copyFromLocalFile(true,true,new Path("d:/Maven_Repository.zip"),new Path("/hadoop32"));
    }

    //文件的下载
    @Test
    public void copyToLocalFile() throws Exception {
        fs.copyToLocalFile(true,new Path("/hadoop32/Maven_Repository.zip"),new Path("d:/y.zip"));
    }

    @Test
    public void testMkdirAndDeleteAndRename() throws IllegalArgumentException, IOException {
        // 创建目录
        fs.mkdirs(new Path("/a1/b1/c1"));
        // 删除文件夹 ,如果是非空文件夹,参数2必须给值true
        fs.delete(new Path("/aaa"), true);
        // 重命名文件或文件夹
        fs.rename(new Path("/a1"), new Path("/a2"));
    }
}

 

上一篇: 前端Mock数据

下一篇: 跨域访问