HDFS(Hadoop Distribute File System)

一、基本概念

     一句话概括: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副本机制)

    

  7、一次写入多次读取

  8、限额操作

数量限额
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 写过程

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读过程

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"));
    }
}

 

posted @ 2019-10-24 18:50  我の女王  阅读(286)  评论(0编辑  收藏  举报