• Hadoop之hdfs


    HDFS

    HDFS概述

    Hadoop Distributed File System

    一种统一管理多个节点上的文件的分布式系统。

    使用场景:适合一次写入,多次读出的场景,且不支持文件修改。适合用来做数据分析,并不适合做网盘应用。

    HDFS优点

    1)高容错性

    数据自动保存多个副本。它通过增加副本的形式,提高容错性。某一个副本丢失后,可以自动恢复。

    2)适合处理大数据

    1、数据规模:能够处理GB、TB、甚至PB级别的数据。

    2、文件规模:能够处理百万规模以上的文件数量。

    3)可构建在廉价机器上,通过多副本机制,提高可靠性。

    HDFS缺点

    1)不适合低延时数据访问,比如毫秒级的存储数据。

    2)无法高效的对大量小文件进行存储。

    1、存储大量小文件的话,会占用NameNode大量内存来储存文件目录和快信息。

    2、小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。

    3)不支持并发写入、文件随机修改

    1、一个文件只能有一个写,不允许多个线程同时写。

    2、仅支持数据append(追加),不支持文件的随机修改。

     

    HDFS组成架构

     

    HDFS文件块大小**

    为什么块大小不能设置太小,也不能设置太大?

    1、HDFS块大小设置太小,会增加寻址时间,程序一直在找块的开始位置。

    2、如果块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。导致程序在处理这块数据时,会非常慢。

    总结:HDFS的块大小设置主要取决于磁盘传输速率。

    (可通过修改配置hdfs-site.xml参数dfs.blocksize(默认值134217728即128m)来修改块大小)

    HDFS常用命令(linux)

    基本语法

    #两个命令相同
    bin/hadoop fs xxx
    bin/hdfs dfs xxx

    命令大全

    #可通过hadoop fs -help xx查看对应命令详情
    Usage: hadoop fs [generic options]
    [-appendToFile <localsrc> ... <dst>]
    [-cat [-ignoreCrc] <src> ...]
    [-checksum <src> ...]
    [-chgrp [-R] GROUP PATH...]
    [-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
    [-chown [-R] [OWNER][:[GROUP]] PATH...]
    [-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] <localsrc> ... <dst>]
    [-copyToLocal [-f] [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
    [-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] <path> ...]
    [-cp [-f] [-p | -p[topax]] [-d] <src> ... <dst>]
    [-createSnapshot <snapshotDir> [<snapshotName>]]
    [-deleteSnapshot <snapshotDir> <snapshotName>]
    [-df [-h] [<path> ...]]
    [-du [-s] [-h] [-v] [-x] <path> ...]
    [-expunge]
    [-find <path> ... <expression> ...]
    [-get [-f] [-p] [-ignoreCrc] [-crc] <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 <localsrc> ... <dst>]
    [-moveToLocal <src> <localdst>]
    [-mv <src> ... <dst>]
    [-put [-f] [-p] [-l] [-d] <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 -[defsz] <path>]
    [-text [-ignoreCrc] <src> ...]
    [-touch [-a] [-m] [-t TIMESTAMP ] [-c] <path> ...]
    [-touchz <path> ...]
    [-truncate [-w] <length> <path> ...]
    [-usage [cmd ...]]

    常用命令

    上传
    #从本地剪切粘贴到HDFS
    -moveFromLocal <localsrc> ... <dst>
    #从本地拷贝粘贴到HDFS
    -copyFromLocal <localsrc> ... <dst>
    #追加一个文件到已经存在的文件末尾
    -appendToFile <localsrc> ... <dst>
    #等同于copyFromLocal
    -put <localsrc> ... <dst>
    下载
    #从HDFS拷贝到本地
    -copyToLocal <src> ... <localdst>
    #等同于copyToLocal
    -get <src> ... <localdst>
    #合并下载多个文件(注意该命令全小写)
    -getmerge <src> ... <localdst>
    其他文件操作
    #等同于liunx
    -ls
    -mkdir
    -cat
    -chgrp、-chmod、-chown
    -cp
    -mv
    -tail
    -rm
    -rmdir
    #统计文件夹大小
    -du [-s 合并显示总大小] [-h 格式化显示大小单位] [-v] [-x] <path> ...
    #设置HDFS中文件的副本数量(若文件副本数量大于节点数量,会在新增加节点时自动增加副本)
    -setrep <rep 数量> <path 文件路径> ...

     

    HDFS客户端操作(Windows)**

    HDFS客户端环境准备

    #环境变量
    依赖文件:hadoop-3.1.0
    配置HADOOP_HOME环境变量
    #若有问题可将hadoop-3.1.0的bin目录下的hadoop.dll和winutils.exe放到C:/windows/system32目录下。

    HDFS配置MAVEN工程依赖

    创建一个Maven工程并添加依赖坐标(pom.xml)

    <dependencies>
       <dependency>
           <groupId>junit</groupId>
           <artifactId>junit</artifactId>
           <version>4.12</version>
       </dependency>
       <dependency>
           <groupId>org.apache.logging.log4j</groupId>
           <artifactId>log4j-slf4j-impl</artifactId>
           <version>2.12.0</version>
       </dependency>
       <dependency>
           <groupId>org.apache.hadoop</groupId>
           <artifactId>hadoop-client</artifactId>
           <version>3.1.3</version>
       </dependency>
    </dependencies>

    在src/main/resources目录下新建文件log4j2.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="error" strict="true" name="XMLConfig">
       <Appenders>
           <!-- 类型名为Console,名称为必须属性 -->
           <Appender type="Console" name="STDOUT">
               <!-- 布局为PatternLayout的方式,
               输出样式为[INFO] [2018-01-22 17:34:01][org.test.Console]I'm here -->
               <Layout type="PatternLayout"
                       pattern="[%p] [%d{yyyy-MM-dd HH:mm:ss}][%c{10}]%m%n" />
           </Appender>
       </Appenders>
       <Loggers>
           <!-- 可加性为false -->
           <Logger name="test" level="info" additivity="false">
               <AppenderRef ref="STDOUT" />
           </Logger>
           <!-- root loggerConfig设置 -->
           <Root level="info">
               <AppenderRef ref="STDOUT" />
           </Root>
       </Loggers>
    </Configuration>

    HDFS的API操作

    创建文件系统对象
    // 方式一:
    /*
    uri : HDFS的地址(NameNode的地址)
       conf : 配置内容
       user : 操作集群的用户
    */
    FileSystem fs = FileSystem.get(final URI uri, final Configuration conf,final String user)
     
    // 方式二:
    /*
    1.需要在Configuration中配置HDFS的路径
       2.先运行一下该类(否则找不到)
       选中对应类的配置 ----> ditConfigurations ----> VM Options ---->填入-DHADOOP_USER_NAME=atguigu
    */
    Configuration conf = new Configuration();
    //需要配置HDFS的地址
    conf.set("fs.defaultFS","hdfs://hadoop102:9820");
    FileSystem fs = FileSystem.get(conf);

    // 拓展:main(String[] args)方法参数由控制台传递,也可在idea中edit configurations中的program arguments中设置,参数由空格隔开。
    // 控制台: java xxx.class(字节码文件) arg1 arg2 ...
    // idea:program arguments[ arg1 arg2 ...]
    常用文件系统操作

    上传

    /*
    delSrc : 是否删除源文件(本地)
       overwrite : 如果目标文件已经存在是否覆盖目标文件(HDFS)如果为false : 如果目标文件存在则报错
       src : 源文件路径(本地)
       dst : 目标文件路径(HDFS)
    */
    fs.copyFromLocalFile((boolean delSrc, boolean overwrite,Path src, Path dst)

    下载

    /*
    delSrc : 是否删除源文件(HDFS)
        src :源文件路径(HDFS)
        dst : 目标文件路径(本地)
        useRawLocalFileSystem :是否使用RawLocalFileSystem
    */
    fs.copyToLocalFile(boolean delSrc, Path src, Path dst,boolean useRawLocalFileSystem)

    删除文件夹

    /*
    f : 要删除的文件和目录的路径
    recursive : 是否递归删除
    注意:如果是删除目录的话该值只能为true否则报错
    如果是文件(空目录)该值既可以为true也可以为false
    */
    fs.delete(Path f, boolean recursive)

    修改文件名

    /*
    src : 源文件路径(HDFS)
    dst : 目标文件路径(HDFS)
    */
    fs.rename(Path src, Path dst)

    // 修改文件名
    fs.rename(new Path("/aaa.txt"),new Path("/ccc.txt"));

    // 移动文件
    fs.rename(new Path("/ccc.txt"),new Path("/output"));

    查看文件详情

    /*
        f : 是文件或目录的路径
        recursive :是否递归
    */
    RemoteIterator<LocatedFileStatus> remoteIterator = fs.listFiles(final Path f, final boolean recursive)
    // 遍历
    while(remoteIterator.hasNext()){
           //获取文件
           LocatedFileStatus fileStatus = remoteIterator.next();
           System.out.println("====================" + fileStatus.getPath().getName()+ "=====");
           System.out.println("文件名:" + fileStatus.getPath().getName());
           System.out.println("副本数" + fileStatus.getReplication());

           //获取块信息
           BlockLocation[] blockLocations = fileStatus.getBlockLocations();
           System.out.println(Arrays.toString(blockLocations));
    }

    查看文件的类型

     /*
    f : 目标文件或目录的路径
    */

    FileStatus[] status = this.fs.listStatus(Path f);
           //遍历
           for (FileStatus f : status) {
               if (f.isFile()){
                   System.out.println(f.getPath().getName() + "是一个文件");
              }else if (f.isDirectory()){
                   System.out.println(f.getPath().getName() + "是一个目录");
              }
          }

    使用流上传或下载文件

    上传

    //输入流 - 文件输入流
    FileInputStream fis = new FileInputStream("D:\io\hdfs\aa.txt");
    //输出流 - 向HDFS上输出的流
    FSDataOutputStream fos = fs.create(new Path("/aa.txt"));
    //一边读一边写
    /*
    copyBytes(InputStream in, OutputStream out,int buffSize, boolean close)
    in :输入流
    out :输出流
    buffsize :缓冲区大小
    close : 是否关闭流
    */
    IOUtils.copyBytes(fis,fos,1024,false);
    //关流
    IOUtils.closeStream(fis);
    IOUtils.closeStream(fos);

    下载

    //输入流 - 从HDFS上输入的流
    FSDataInputStream fis = fs.open(new Path("/aa.txt"));
    //输出流 - 文件输出流
    FileOutputStream fos = new FileOutputStream("D:\io\hdfs\aa.txt");
    //一边读一边写(文件对拷)
    IOUtils.copyBytes(fis,fos,1024,true);

    配置的优先级

    配置的位置

    1. xxx.default.xml

    2. xxx.site.xml

    3. maven工程src/main/resources中xxx.site.xml

    4. 代码中Configuration做配置

    配置的优先级

    4 > 3 > 2 > 1

     

    HDFS读写数据流程**

    HDFS写数据流程

    1. Client创建FileSystem对象,向NameNode请求上传文件,NameNode检查文件是否存在,文件目录是否存在,判断用户是否有权限

    2. NameNode响应可以上传

    3. Client切分文件块,并向NameNode上传第一个Block,请求NameNode返回DataNode节点

    4. NameNode返回dfs.replication数量的节点

    5. Client创建输出流,向dn1请求建立Block传输通道,成功后继续向dn2请求,成功后向dn3请求

    6. dn3,dn2,dn1返回成功应答

    7. Client向dn1以数据包Package的形式上传数据,dn1往内存中Bytebuffer写入的同时,写到dn1本地磁盘,同时内存中Bytebuffer向dn2的Bytebuffer中上传...

      // Package传输时放入应答队列等待应答

      // 若传输途中有dn节点宕机,则会跳过此节点,传输完成后,NameNode重新制定dn存储副本,该节点重启后,删除未完成的传输内容

    节点距离计算与机架感知

    机架感知官方说明地址:http://hadoop.apache.org/docs/r3.1.3/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Data_Replication

    2.x版本:在同一机架上选择第二个节点

    3.x版本:在不同机架上选择第二个节点,并在考虑到效率问题,在第二个节点所在机架上选择第三个节点,若节点数超过3个,则完全随机。

    HDFS读数据流程

    1. Client创建FileSystem对象,向NameNode请求下载文件,NameNode判断是否有该文件,判断该用户是否有权限

    2. NameNode返回目标文件的元数据

    3. Client创建流,挑选一台DataNode(就近原则,然后随机)逐个找DataNode读取数据块

    4. DataNode从磁盘读取数据输入流,以Packet为单位做校验

    5. Client以Packet为单位接收数据,先在本地缓存,然后写入文件

     

    NameNode和SecondaryNameNode**

    NN和2NN工作机制(CheckPoint)

    1. Client发出请求

    2. Namenode收到请求后,将命令记录到滚动日志edits_inprogress_xx,并在内存中执行该命令

      // NameNode启动时,会将edits_inprogress_xx和fsimage读到内存

    3. 2NN不停向NN询问是否需要CheckPoint(配置在hdfs.default.xml中)

      // dfs.namenode.checkpoint.check.period 默认为60s

      当CheckPoint条件触发(配置在hdfs.default.xml中)

      // dfs.namenode.checkpoint.period 默认为3600s

      // dfs.namenode.checkpoint.txns 默认为1000000

    4. NameNode中将edits_inprogress_xx文件重命名为edits_xxx并且新建一个edits_inprogress_xx2文件继续存放滚动日志

    5. 将重命名后的edits_xxx文件及fsimage文件从磁盘拷贝到2NN,2NN会在内存中执行edits_xxx生成元数据并与fsimage中的元数据合并,生成文件fsimage_chkpoint

    6. 将2NN中fsimage_chkpoint拷贝到NN中,并重命名为fsimage,至此一个CheckPoint完成

    拓展:

    若NameNode故障,可紧急将2NN的数据(/opt/module/hadoop-3.1.3/data/tmp/dfs/name)拷贝到NameNode对应目录中,重启NameNode即可。

    集群安全模式

    NameNode启动

    NameNode启动时,将Fsimage文件加载进内存,并执行Edits_inprogress_xx中的操作,在内存中建立元数据映像成功后,将创建新的Fsimage和一个空的滚动日志编辑文件,然后开始监听DataNode请求,过程中,NameNode一直处于安全模式,NameNode的文件对客户端说是只读的。

    DataNode启动

    HDFS系统中数据块的位置信息并不是由NameNode维护的,而是以块列表的形式存在DataNode中,在系统正常操作期间,NameNode会在内存中保留所有的块位置的信息。在安全模式下,各个DataNode会向NameNode发送最新的块列表信息,NameNode了解到足够多的快信息后,即可高效运行文件系统。

    安全模式退出判断

    最小副本条件:hdfs.default.xml 中

    dfs.namenode.replication.min 默认值为1

    如果满足最小副本条件,NameNode会在30秒之后退出安全模式。最小副本条件是指99.9%的块满足最小副本级别。在启动一个刚刚格式化的HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式。

    #安全模式命令
    #查看安全模式状态
    bin/hdfs dfsadmin -safemode get
    #进入安全模式
    bin/hdfs dfsadmin -safemode enter
    #离开安全模式
    bin/hdfs dfsadmin -safemode leave
    #等待安全模式结束
    bin/hdfs dfsadmin -safemode wait

    DataNode工作机制**

    DataNode启动后,向NameNode注册,报告该节点所存放的块信息,并且每周期(1小时)上报一次所有块信息。

    DN心跳机制

    心跳间隔:hdfs-default.xml

    dfs.heartbeat.interval 默认为3s

    Timeout计算公式:

    Timeout=2*dfs.namenode.heartbeat.recheck-interval+10*dfs.heartbeat.interval

    dfs.namenode.heartbeat.recheck-interval 默认为300000ms(5分钟)

    dfs.heartbeat.interval 默认为3s

    DataNode每3秒向NameNode发送心跳以证明该节点存活,心跳返回的结果带有NameNode给DataNode的命令,若NameNode超过10分钟30秒没收到某节点的心跳,则认为该节点不可用。

    DN数据完整性

    1. DataNode读取Block的时候,会计算CheckSum

    2. 如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏

    3. Client读取其他DataNode上的Block

    4. DataNode在其文件创建后周期验证CheckSum

    数据传递时是以Package包形式,包中携带chunk(由512字节+4字节(32位))作为检验和

    服役新数据节点

    #配置hostname
    sudo vim /etc/hostname
    #配置hosts
    sudo vim /etc/hosts
    #配置hadoop配置文件(使用同步脚本)
    #直接启动DataNode即可关联到集群
    hdfs --daemon start datanode
    #如果数据不均衡,可以使用命令实现集群的再平衡
    $HADOOP_HOME/sbin/start-balancer.sh

    #补充:$HADOOP_HOME/etc/hadoop/workers中配置的是$HADOOP_HOME/sbin/start-dfs.sh脚本使用的集群配置,单独添加一台DN节点,只需配置好NameNode节点信息,就可加入集群

    #注意:修改hostname需重启linux

    白名单与黑名单

    白名单

    1. 配置NameNode中hdfs-site.xml

    <!-- 增加dfs.hosts属性 在目录创建对应文件 -->
    <property>
    <name>dfs.hosts</name>
    <value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts</value>
    </property>
    1. 文件添加白名单节点(末行不得有空行)

    hadoop102
    hadoop103
    hadoop104
    1. 分发配置

    xsync hdfs-site.xml
    1. 刷新NameNode

    hdfs dfsadmin -refreshNodes
    1. 更新ResourceManager节点

    yarn rmadmin -refreshNodes
    1. 如果数据不均衡,可以用命令实现集群的再平衡

    $HADOOP_HOME/sbin/start-balancer.sh

    注意:直接移除白名单并刷新节点,会导致被移除的节点DataNode进程直接关闭,无法实行退役操作,即该节点数据无法迁移到别的节点。

     

    黑名单

    1. 配置NameNode的hdfs-site.xml配置文件

    <!-- 增加dfs.hosts.exclude属性 在目录创建对应文件 -->
    <property>
      <name>dfs.hosts.exclude</name>
      <value>/opt/module/hadoop-3.1.3/etc/hadoop/dfs.hosts.exclude</value>
    </property>
    1. 文件添加黑名单节点(末行不得有空行)

    hadoop105
    1. 刷新NameNode

    hdfs dfsadmin -refreshNodes
    1. 更新ResourceManager节点

    yarn rmadmin -refreshNodes
    1. 检查web(hadoop102:9870),该节点状态为decommission in progress(退役中),说明该阶段数据块正复制到其他节点。

    2. 等待节点状态为decommissioned,表示已经复制完,停止该节点资源管理器。

    3. 如果数据不均衡,可以用命令实现集群的再平衡

    $HADOOP_HOME/sbin/start-balancer.sh      

    注意:如果退役会导致服役副本节点数小于配置副本数,则不能退役成功,需修改副本数。

    DataNode多目录配置

    DataNode也可以配置成多个目录存储数据,每个目录存储的数据不一样。即:数据不是副本。使用场景为增加硬盘时,扩容。

    配置hdfs-site.xml

    <!-- 配置后根据情况是否分发,重启该DN节点,会自动创建文件夹 -->
    <property>
       <name>dfs.datanode.data.dir</name>
       <value>file:///${hadoop.tmp.dir}/dfs/data1,file:///${hadoop.tmp.dir}/dfs/data2</value>
    </property>

     

  • 相关阅读:
    html实现打印预览效果
    layui-table 对表格数据进行处理之后的排序问题
    layui-table与layui-rate评分转换成星级的使用
    使用apache的poi来实现数据导出到excel的功能——方式二
    java获取配置文件中的key=value值
    layDate——设置最大日期不能超过当前日期
    layDate——初步使用
    echarts使用——柱状图
    layui内部定义的function,外部调用时候,提示某函数未定义现象解决方案
    layui table异步调用数据的时候,数据展示不出来现象解决方案
  • 原文地址:https://www.cnblogs.com/BookMiki/p/15003919.html
Copyright © 2020-2023  润新知