Hadoop-HDFS API+工作机制简述

HDFS

HDFS概述

这是一种分布式的文件系统,用于管理在不同机器上的文件存储,通过目录树的方式来定义文件存储位置。

适合一次写入,多次读出的情况。一个文件的修改只能在末尾追加,而不能在中间修改。

HDFS的优缺点

优点

  1. 高容错性:数字键保存多个副本,提高容错性,丢失一些副本后可以自动恢复
  2. 适合大数据处理
    • 数据规模:处理GB到PB级别的数据
    • 文件规模:处理百万数量的文件数量
  3. 可以构建在廉价机器上

缺点

  1. 不适合低延时的数据访问,如毫秒级的存储数据是做不到的
  2. 无法高效对大量小文件存储
    • 小文件也会占用很多NameNode和大量的内存来存储文件目录和块信息
    • 小文件的寻址时间会超过读取时间,违反了HDFS的设计原则
  3. 不支持并发写入、文件随机修改
    • 写操作只允许单线程写
    • 仅支持数据追加,不支持数据修改

HDFS组成架构

  1. NameNode(nn):master,管理HDFS的信息
    • 管理名称空间
    • 设置副本的测量
    • 管理数据块(Block)的映射信息
    • 处理客户端的读写请求
  2. DataNode:slave,实际进行数据的操作
    • 存储实际的数据块
    • 进行数据块的读写操作
  3. Secondary NameNode:不是NameNode的备份,当NameNode宕机时,不能立即替换NameNode进行工作
    • 辅助NameNode,分担工作量,如定期合并Fsimage和Edits并且推送给NameNode
    • 紧急情况下,可以辅助恢复NameNode
  4. Client:客户端
    • 文件切分:文件上传HDFS时,Client将文件切分成Block大小再上传,通常Block就是128M或256M
    • 与NameNode交互,获取文件的位置信息
    • 与DataNode交互,读取或者写入数据
    • 提供一些命令来管理HDFS,比如HDFS进行增删改查

HDFS文件块大小(面试)

HDFS的文件在物理上是分块存储的(Block),这个参数指该块中最多存储的数据大小,默认为128M。

  1. 集群中存储的数据都是按照block的
  2. 查找文件所在块的时间定义为寻址时间,约为10ms
  3. 寻址时间为传输时间的1%时的状态是最佳的,即传输时间是寻址时间的100倍,约为1s
  4. 一般的机械硬盘中,设置为128M即可,因为传输速度大约为100M/s。如果设备较好,可以使用256M。

块大小怎么设置合适?

  1. 如果HDFS的块太小,会增加寻址时间,寻找Block都要很长时间
  2. 如果HDFS块太大,传输的开销太大

HDFS相关Shell操作

hadoop/hdfs fs -command

这是基础的命令前缀。HDFS和Linux命令也非常相似。

  • 上传

    • -moveFromLocal src dst 从本地剪切粘贴到HDFS
    • -copyFromLocal src dst 本地拷贝到HDFS
    • -put 和上一条完全相同
    • -appendToFile src dst 追加一个文件到已存在的文件末尾
  • 下载

    • -copyToLocal src dst 把文件拷贝到本地
    • -get 等同于上一条,dst中可以对文件重命名
  • 其他操作

    • -ls 查询根目录

    • -cat 查询文件内容

    • -chgrp / -chmod / chrown 更改文件权限

    • -mkdir 创建文件夹

    • -cp src dst 在HDFS中的拷贝

    • -mv src dst 在HDFS中的剪切

    • -tail filename 查看一个文件末尾1kb的数据

    • -rm 删除文件

    • -rm -r 递归删除文件夹内全部内容

    • -du 统计文件夹的大小

      • -s 显示总信息
      • -h 分文件显示信息
    • -setrep num file设置HDFS中一个文件副本的数量

      副本在一台服务器只能有一个,如果副本期望数量高于集群机器数量,则会在集群扩展时增加副本数量

HDFS的API操作

  • 在Windows中配置Hadoop的相关API组件https://github.com/steveloughran/winutils/tree/master

  • 在idea中新建Maven工程

    1. 引入Maven依赖

          <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>
      
    2. 编写客户端相关代码

      首先创建工具类来创建客户端和连接,然后使用一些api进行操作即可。

      /**
       * 1.获取一个客户端对象
       * 2.执行相关操作命令
       * 3.关闭资源
       */
      public class HdfsClient {
      
          private FileSystem fileSystem;
      
          @Before
          public void init() throws URISyntaxException, IOException, InterruptedException {
              //        使用hdfs协议连接hadoop服务器
              URI uri = new URI("hdfs://hadoop102:8020");
      //         创建配置
              Configuration configuration = new Configuration();
      //      设置用户名
              String user = "fantank";
      //        创建FileSystem对象,访问HDFS
              fileSystem = FileSystem.get(uri, configuration, user);
          }
      
          @After
          public void close() throws IOException {
              fileSystem.close();
          }
      
      
          @Test
          public void testMkdir() throws IOException {
              fileSystem.mkdirs(new Path("/huaguoshan"));
          }
      
          @Test
          public void testUpload() throws IOException {
      //        public void copyFromLocalFile(boolean delSrc, boolean overwrite,
      //                                Path src, Path dst)
      //        The src file is on the local disk. Add it to the filesystem at the given dst name. delSrc indicates if the source should be removed
      //        Params:
      //        delSrc – whether to delete the src
      //        overwrite – whether to overwrite an existing file
      //        src – path
      //        dst – path
              fileSystem.copyFromLocalFile(false, true, new Path("C:\\Users\\ARK_LIU\\Pictures\\Cyberpunk 2077\\95803645_p0_master1200.jpg"),
                      new Path("hdfs://hadoop102/arknights.jpg"));
          }
      }
      
    3. 配置hadoop相关参数

      可以在resource目录中,配置和hadoop虚拟机中一样的配置文件,而api配置的优先级是高于hadoop内部配置的。

      <!-- hdfs-site.xml-->
      <configuration>
          <property>
              <name>dfs.replication</name>
              <value>1</value>
          </property>
      </configuration>
      

      如果需要设置参数,也可以通过创建客户端的Configuration来设置参数。

              Configuration configuration = new Configuration();
              configuration.set("dfs.replication", "2");
      

      优先级是按照就近原则的,代码中的设置优先级最高,其次是应用配置文件,最后是服务器配置文件。

  • 文件下载操作

    下载时,默认会携带一个crc文件作为校验数据,保证数据传输的正确性

        @Test
        public void testDownload() throws IOException {
            fileSystem.copyToLocalFile(false, new Path("hdfs://hadoop102/arknights.jpg"), new Path("C:\\Users\\ARK_LIU\\Pictures\Ark"), false);
        }
    

    最后一个参数为是否使用校验,可以关闭。

  • 文件删除操作

        @Test
        public void testDelete() throws IOException {
            //删除文件和文件夹,递归删除
            fileSystem.delete(new Path("/output2"), true);
        }
    
  • 文件改名和移动

        @Test
        public void testMv() throws IOException {
            //更名
            fileSystem.rename(new Path("/ling.jpg"), new Path("/ark.jpg"));
            //文件移动
            fileSystem.rename(new Path("/ark.jpg"), new Path("/input/ling.jpg"));
        }
    
  • 获取文件详情和块信息

    需要使用迭代器

        public void testListFiles() throws IOException {
            //递归获取文件列表
            RemoteIterator<LocatedFileStatus> listFiles = fileSystem.listFiles(new Path("/"), true);
            while (listFiles.hasNext()) {
                LocatedFileStatus fileStatus = listFiles.next();
                System.out.println("------------" + fileStatus.getPath());
                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());
            }
        }
    

    块信息会返回块的起始位置,以及其存储在的服务器的hostname

  • 判断是文件还是文件夹

        @Test
        public void testIsFile() throws IOException {
            FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
    
            for(FileStatus fs : fileStatuses) {
                String path = fs.getPath().getName();
                path += fs.isFile() ? "\tis File" : "\tis Folder";
                System.out.println(path);
            }
        }
    

HDFS读写流程(面试)

HDFS写数据流程
  1. 客户端上传数据,先通过HDFS客户端创建一个分布式文件系统,向NameNode进行文件上传请求;

  2. NameNode需要检测用户的权限(所有者和组)以及目录结构(是否存在)等。检查完毕后响应可以上传文件。

  3. 客户端收到响应后。请求上传第一个Block(0 - 128M),需要NameNode返回存储数据的DataNode来传输数据

  4. NameNode需要选择对应的节点并返回,此时需要考虑节点距离最近、节点是否可用以及负载均衡等因素,向客户端返回DataNode

  5. 客户端接收DataNode。开始创建FSDataOutputStream流,向DataNode发送数据。客户端需要请求DataNode建立Block的传输通道。这个通道是由DataNode之间依次连接的。

  6. 通道建立完毕后,DataNode应答消息由建立起来的传输通道中的DataNode节点链表依次返回给客户端。

  7. 客户端接收应答后,开始向自己连接的DataNode传输文件,由DataNode依次向下一个DataNode传输数据,同时也向本地存储文件。

    传输数据以Packet(64k)为一个单位,其中由最小单位chunk(512byte数据)和chunksum(4byte校验位)组成。

    发送的同时,客户端在本地存储一个Ack队列,当DataNode依次返回某个Packet应答成功,才会在ack队列中删除。

网络拓扑——节点距离计算

节点距离:两个节点到达最近共同祖先的距离和

  • 任何机架内的机器到本机架的距离都是1
  • 机架到集群,集群到互联网的距离也是1
  • 同一机架内的机器不能直接相连,需要到达机架后再相连
机架感知(副本存储的节点选择)

如果是三个副本:

  1. 第一个副本存储在Client所在的DataNode上,如果Client不在DataNode就随机选一个存储第一份
  2. 第二份要尽量和第一份不在同一个Rack的随机一个节点,如果只有一个Rack就只能在本地机架中随机选择一个
  3. 第三份应该和第二份放在同一个机架中,但如果第二份和第一份在同一个Rack,第三份会尝试放到其他Rack中
HDFS读数据流程
  1. 客户端创建分布式文件系统对象,向NameNode请求文件的下载
  2. NameNode进行权限校验和判断是否文件存在,如果存在则返回目标文件的元数据(哪些DataNode存放了该文件的哪些块)
  3. 客户端根据就近原则选取一个DataNode开始读数据,但是DataNode可以根据负载均衡情况,可以将部分块的请求转发到其他DataNode来进行读取。但这种读取在一个文件的读取中,只能是串行完成的。
  4. 客户端串行读取和合并数据,完成后关闭资源即可。

NameNode和SecondaryNameNode

NN和2NN工作原理

NN的更新原理和Redis的AOF相似。

  1. 内存中的元数据是通过fsimage数据直接加载的
  2. 当客户端发送请求,使得NN的元数据发生改变时。首先将操作追加记录在edits_inprogress(工作中的日志)
  3. 2NN会定期询问NN的日志是否记录满了,并且也会定时向NN询问是否执行CheckPoint操作
  4. 开始执行CheckPoint后,工作中的Edits改为一个新的日志,原来的工作日志取消inprogress标识表示该日志已经写完了。之后的读写操作记录在新日志中。
  5. 2NN拉取NN的fsimage和已经完成的edits日志,在内存中进行合并,并且生成新的fsimage.checkpoint作为合并后的新元数据。
  6. 2NN将checkpoint文件发送到NN,使得该文件覆盖原来的fsimage成为新的元数据映像即可
  7. NN加载新的元数据映像到内存
Fsimage和Edits
  • Fsimage是HDFS文件系统元数据的永久性检查点,包含了HDFS文件系统的所有目录和文件inode序列化信息
  • Edits是存放文件系统所有更新操作的路径,文件系统客户端执行的所有写操作都要先放入Edits中
  • seen_txid(data/dfs/name//current/seen_txid),保存了edits_的最后一个数字,即记录了edits到哪一个编号了
  • 每次NamNode启动时,会合并Fsimage和edits的数据,保证数据是最新的
查看文件的内容
  1. Fsimage

    基本语法hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后的文件输出路径

    hdfs oiv -p XML -i  fsimage_0000000000000000387 -o /home/fs/fsimage.xml
    
    <inode><id>16391</id><type>DIRECTORY</type><name>staging</name><mtime>1685507901738</mtime><permission>fantank:supergroup:0755</permission><nsquota>-1</nsquota><dsquota>-1</dsquota></inode>
    ......
    <directory><parent>16390</parent><child>16391</child></directory>
    

    这里是按照树形结构来进行管理目录和文件的关系的,可以通过id和child以及parent标签找到上下的路径。

    这里不存储文件块在其他服务器的的信息,而是DataNode向NameNode发送相关的信息。

  2. 查看Edits文件

    基本语法hdfs oev -p 文件类型 -i 编辑日志 -o 转换后的文件输出路径

      <RECORD>
        <OPCODE>OP_RENAME_OLD</OPCODE>
        <DATA>
          <TXID>395</TXID>
          <LENGTH>0</LENGTH>
          <SRC>/home/fsimage.xml._COPYING_</SRC>
          <DST>/home/fsimage.xml</DST>
          <TIMESTAMP>1685601081562</TIMESTAMP>
          <RPC_CLIENTID>85930b02-232f-471e-a6ad-b41bef16d1d6</RPC_CLIENTID>
          <RPC_CALLID>8</RPC_CALLID>
        </DATA>
      </RECORD>
    

    这个文件中通过RECORD标签记录文件系统修改信息。

    每次开机时,fsimage后跟随的序号代表此前的全部edits已经合并过了,就不需要再次合并了。

    2NN和NN的差距在于,NN中存储了最新的edit信息,而2NN只有每次checkpoint合并后的数据。

检查点的时间设置

Checkpoint的默认时间是3600秒,在hdfs-default.xml中有设置;

而操作检查是每1分钟进行一次询问,如果到达100万次修改则进行一次Checkpoint。

一般NN会使用2个达成高可用,2NN用的不多。

DataNode

DataNode工作机制
  1. DataNode启动后,主动向NameNode进行信息汇报,信息包括块的数据、数据长度、校验和、时间戳等

  2. NameNode收到汇报后,在本地注册DataNode的信息

  3. DataNode每隔一段时间向NameNode汇报块的健康信息,默认6小时一次;并且DataNode需要每3秒向NameNode通过心跳机制发送存活信息。

    <property>
    	<name>dfs.blockreport.intervalMsec</name>
    	<value>21600000</value>
    	<description>Determines block reporting interval in milliseconds.</description>
    </property>
    

    DN也会自查自己的块健康情况,也是6小时检查一次。

    <property>
    	<name>dfs.datanode.directoryscan.interval</name>
    	<value>21600s</value>
    	<description>Interval in seconds for Datanode to scan data directories and reconcile the difference between blocks in memory and on the disk.
    	Support multiple time unit suffix(case insensitive), as described
    	in dfs.heartbeat.interval.
    	</description>
    </property>
    
  4. 如果一个DataNode超过10分钟+30秒没有心跳,则认为该节点不可用,从而不再与该机器进行交互

数据完整性校验

HDFS使用的是循环冗余校验CRC32的,如使用copyToLocal时,可以选择是否开启校验。如果开启校验则会下载一个校验文件。

CRC32的基本原理和过程:

  1. 选择生成多项式:CRC32使用一个32位的生成多项式。常用的CRC32生成多项式是0x04C11DB7
  2. 数据扩展:与其他CRC算法类似,CRC32也需要对要传输的数据进行扩展。扩展过程是将数据左移32位,并在右侧填充32个零。这样扩展后的数据位数与生成多项式相同。
  3. CRC计算:使用生成多项式进行CRC计算。将扩展后的数据与生成多项式进行除法运算(模2除法),按位执行异或操作。除法运算的结果是一个32位的余数,即CRC校验码。
  4. 附加校验码:将计算得到的32位CRC校验码附加到原始数据之后,形成最终的传输数据。这样,发送方就能将数据和校验码一起发送给接收方。
  5. 接收方校验:接收方收到数据后,执行与发送方相同的CRC计算过程,使用相同的生成多项式。如果接收到的数据在校验过程中产生的余数为零,说明数据没有错误。否则,接收方会检测到错误,丢弃接收到的数据或者请求重新传输。
掉线时限参数
  1. DataNode掉线后,心跳机制无法在DataNode和NameNode之间通信

  2. NameNode会默认在10分钟+30秒的区间内等待心跳恢复,这段时间是超时时长,计算公式是 2倍的心跳重检测时长+10倍心跳间隔;两者的默认值是5分钟(毫秒单位)和3秒,所以得到10分30秒

    <property>
        <name>dfs.namenode.heartbeat.recheck-interval</name>
        <value>300000</value>
    </property>
    
    <property>
        <name>dfs.heartbeat.interval</name>
        <value>3</value>
    </property>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值