Hadoop HDFS文件系统
阅读原文时间:2021年04月20日阅读:1
HDFS详解(性能,架构,基本储存单元,读写分析,组成,SNN合并,安全模式)

性能

HDFS 天生是为大规模数据存储与计算服务的,而对大规模数据的处理目前还有没比较稳妥的解决方案。 HDFS 将将要存储的大文件进行分割,分割到既定的存储块(Block)中进行了存储,并通过本地设定的任务节点进行预处理,从而解决对大文件存储与计算的需求。

  • 在实际工作中,除了某些尺寸较大的文件要求进行存储及计算,更多时候是会产生并存储无数的小尺寸文件。
  • 而对于小尺寸文件的处理,HDFS没有要求使用者进行特殊的优化,也就是说可以通过普通的编程和压缩方式进行解决。
  • 对于大部分文件来说,一旦文件生成完毕,更多的是对文件进行读取而非频繁的修改。
  • HDFS 对于普通文件的读取操作来说,一般情况下主要分成两种:
    大规模的持续性的读取与小型化随机读取。
    针对这两种读取方式,HDFS分别采取了不同对应策略。
  1. 对于大规模的数据读取,HDFS采用的是在存储是进行优化,也就是说在文件进入HDFS的时候,就对较大文件储存就采用集中式储存的方式,使得未来的读取能够在一个文件一个连续的区域进行,从未节省寻址及复制时间。
  2. 对于小数据的读取,HDFS更多的做法在小规模的随机读取操作合并对读取顺序进行排序,这样可以在一定程度上实现按序读取,提高读取效率。因此可以说HDFS更多考虑到数据读取的批处理,而不是对单独命令的执行。

架构与基本储存单元

基础架构

HDFS采用master/worker架构。一个HDFS集群是由一个Namenode和一定数目的Datanodes组成。Namenode是一个中心服务器,负责管理文件系统的命名空间(namespace)以及客户端对文件的访问。集群中的Datanode一般是一个节点一个,负责管理它所在节点上的存储。HDFS暴露了文件系统的命名空间,用户能够以文件的形式在上面存储数据。从内部看,一个文件其实被分成一个或多个数据块,这些块存储在一组Datanode上。Namenode执行文件系统的命名空间操作,比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体Datanode节点的映射。Datanode负责处理文件系统客户端的读写请求。在Namenode的统一调度下进行数据块的创建、删除和复制。

  • 客户端的请求全部落在了NameNode上
  • 元数据信息存在NameNode
  • 在Hadoop集群中有且只有一个处于active的NameNode
  • SecondaryNameNode 不是NameNode的备份节点或从节点(确切的说他只备份NameNode的部分内)
  • NameNode 与 DataNode之间有心跳机制,从而NameNode可以知道DataNode的运行情况与负载情况
    对于HDFS架构来说,一个HDFS基本集群包括两大部分。
    即NameNode 、 DataNode ,其作用是将管理和工作分离,通常来说,一个集群会有一个NameNode和若干个DataNode。
    NameNode中存放的是元数据,即数据类型、大小、格式、以及对象形式存放在NameNode内存中。以便于加快读取速度。
    DataNode是一个集群的主服务器,主要是用于对HDFS中所有的文件及内容数据进行维护,并不断的读取记录集群中DataNode主机情况与工作状态,并通过读取与写入镜像日志文件的方式进行储存。
    而DataNode是在HDFS集群中担任任务具体执行,是整个集群的工作节点,文件被分为若干个相同大小的数据块,分别储存在若干相干DataNode上,DataNode定时定期向集群内NameNode发送自己的运行状态和储存内容,并根据NameNode发送的指令进行工作。

NameNode和 DataNode可以在同一台机器上,但是此种工作方式极大地限制了HDFS的性能

NameNode负责接受客户端发送过来的消息,然后将文件储存信息位置发送给提交请求的客户端,由客户端直接与DataNode进行联系,进行部分文件的运算与操作。对于文件储存来说,HDFS使用Block(分块)来对文件的储存进行操作。对于传统磁盘储存来说,磁盘都有默认的基本储存单元,通常使用的是数据定义中国最小的储存单元。Block是HDFS的基本储存单元,默认大小是64M,这个大小远远大于一般系统文件的默认储存大小。这样的好处就是减少了寻址时间。
除此之外,采用Block对文件进行储存,大大提高了文件的灾难生存与恢复,HDFS还对已经储存的Block进行多副本备份,将每个Block至少复制到3 个相互独立的硬件上。这样做的好处就是确保在发生硬件故障的时候,能够迅速的从其他文件中国读取相应的文件数据。而具体复制到多少个独立硬件上,也是可以设置的。

数据储存位置和复制详解

  • 同一节点上的储存数据
  • 同一机架上不同节点上的储存数据
  • 同一数据中心不同机架上的存储数据
  • 不同数据中心的节点
    HDFS 数据存放策略就是采用同节点与同机架并行的储存方式。在运行客户端的当前节点上存放第一个
    副本,第二个副本存放在与第一个副本不同的机架上的节点,第三个副本的位置在同一个机架上而非同一个节点。

读取过程分析:

  1. 客户端用户通过调用FileSystem对象的open()方法打开需要读取的文件,这对HDFS来说是常见一个分布式文件系统的读取案例。
  2. FileSystem通过远程调用NameNode确定文件的前几个Block的位置。对于每个Block ,NameNode返回一个含有那个Block拷贝的元数据,即文件基本信息;接下来,DataNode按照上文定义的距离值进行排序,如果client本来就是一个DataNode,那么优先从本地DataNode节点读取数据。HDFS实例做完以上工作之后,返回一个FSDataInputStream给客户端,让其从FSDataInputStream中读取数据。FSDataInputStream 接着包装一个FSInputStream用来管理DataNode和NameNode的 I/O。
  3. NameNode 向客户端返回一个包含数据信息的地址,客户端根据地址创建一个FSDataInputStream 开始对数据进行读取。
  4. FSDataInputStream 根据开始时存放的前几个Block 的DataNode的地址,连接到最近的DataNode上岁数据从头开始读取。客户端反复调用read() 方法,以流式方式从DataNode读取数据
  5. 当读到Block的结尾的时候,FSDataInputStream会关闭当前DataNode的链接,然后查找能够读取下一个Block的最好的DataNode,这些操作对客户端是透明的,客户感觉到的是连续的流,也就是说读取的时候就开始查找下一个块所在的地址。
  6. 读取完成调用close()方法,关闭FSDataInputStream。

对于错误处理

在读取期间,当client与DataNode通信的时候如果发生错误的话,他会尝试读取下一个紧接着的含有那个block的DataNode。
Client会记住发生错误的DataNode,这样它就不必在读取以后的块时候再尝试这个DataNode
Client也验证从DataNode传递过来的数据的checksum。如果错误的Block 被发现,他尝试从另一个DataNode读取数据前被报告给NameNode

写入过程分析

  1. client 通过调用FileSystem的create()方法来请求创建文件

  2. FileSystem 通过 NameNode 发出远程请求,在NameNode里面创建一个新的文件,但此时并不关联任何的块。NameNode进行很多检查来保证:1.不存在要创建的文件已经存在于文件系统中 2. 同时检查时候有相应的权限来创建文件。如果这些检查都完成了,那么NameNode将记录下来这个新文件的信息。 FileSystem返回一个FSDataOutputStream 给客户端用来写入数据。和读的情形一样,FSDataOutputStream 将包装一个DFSOutputStream用于和DataNode及NameNode通信。而一旦文件创建失败,客户端会受到一个IOException,标示文件创建失败,停止后续任务。

  3. 客户端开始写数据,FSDataOutputStream 把要写入的数据分成包的形式,将其写到中间队列中。其中的数据由DataStream 来读取。DataStream 的职责就是让NameNode分配一个新的块通过找出合适的DataNode—来储存作为备份而复制的数据。这些DataNode组成一个流水线,假设这些流水线是一个三级流水线,那么里面将含有三个节点。
    此时,DataStream将数据首先写到流水线的第一个节点。此后由第一个节点将数据包传入并写入第二各节点,然后第二个将数据包传送并写入到第三个节点

  4. FSDataOutputStream **维护了一个关于内部关于packets的队列,里面存放等待被DataNode确认无误的packet信息,这个队列称为 等待队列 **,一个packet的信息被移出本队列当且仅当packet被流水线中所有的节点都确认无误。

  5. 当完成数据写入之后客户端调用流的close() 方法,在通知NameNode完成写入之前,这个方法将flush残留的packet,并等到确认信息(acknowlegement) 。
    NameNode已经知道文件都是由哪些块组成(通过DataStream 询问数据块的分配),所以在他返回成功前只需要等待数据块进行最小值复制。

关于写入数据的时候 DataNode 发生错误的处理过程如下

发现错误之后,首先关闭流水线,然后将没有被确认的数据放到数据队列的开头,当前的块被赋予一个新的标识,这个信息将发给NameNode,以便在损坏的数据节点恢复之后删除这个没有被完成的块,然后从流水线中移除损坏的DataNode。
之后将这个块剩下的数据写入到剩下的两个节点中。NameNode注意到这个块的信息还没有被复制完成,他就在其他一个DataNode上安排复制。接下来的Block写入操作就和往常一样了。

基本组成

  • NameNode :接受客户端的读写服务
    执行文件系统命名空间操作:打开,关闭和重命名文件和目录等
    管理文件系统命名空间:记录对文件系统命名空间或其属性的任何修改

  • Metadata(元数据)
    Metadata是储存在NameNode上的元数据信息,它储存到磁盘的文件名名为:fsimage。并且有个叫edits的文件记录对Metadata的操作日志。总体来说,fsimage和edits文件记录了metadata中权限信息和文件系统目录树、文件包括哪些块,确定块到DataNode的映射,Block存放在哪些DataNode上(由DataNode启动时上报)。
    NameNode将这些信息加载到内存并进行拼装,就成为了一个完整的元数据信息

  • 文件系统命名空间
    HDFS支持传统的分层文件组织。用户或应用程序可以在这些目录中创建目录和储存文件。文件系统命名空间层次结构与大多数其他现有文件系统类似:可以创建和删除文件,将文件从一个目录移动到另一个目录,或重命名文件。HDFS支持用户配额和访问权限。但不支持硬链接和软连接。
    NameNode维护文件系统命名空间。对文件系统命名空间或属性的任何更改由NameNode记录。应用程序可以指定应由HDFS维护的文件副本数。文件的副本数称为该文件的复制因子。此信息由NameNode储存。

文件系统的元数据的持久性

NameNode的metadata信息在启动后会加载到内存,由于加载到内存的数据很不安全,断电之后就没有了,因此必须对内存中内存放的信息做持久化处理。
NameNode上保存着HDFS的命名空间。对于任何对文件系统数据产生修改的操作,NameNode都会使用一种被称为edits的事务日志记录下来。
例如,在HDFS中创建一个文件,Namenode就会在Edits中插入一条记录来表示;同样地,修改文件的副本系数也将往Edits插入一条记录。


NameNode在本地操作系统的文件系统中储存这个edits,整个文件系统的命名空间,包括数据块到文件的映射,文件的属性等,都储存在一个称为fsimage的文件中,这个文件也是放在NameNode所在的本地文件系统上。
~NameNode在内存中保存着整个文件系统的命名空间和文件数据块映射

(Blockmap)的映像。~ 这个关键的元数据结构设计得很紧凑,因而一个有4G内存的Namenode足够支撑大量的文件和目录。当NameNode启动时,它从硬盘中读取edits和fsimage,将所有edits中的事务作业在内存中的fsimage上,并将这个新版本的fsimage从内存中保存到本地磁盘上,然后删除就得edits,因为这个旧的edits的事务都已经作用在fsimage上了。这个过程叫做一个检查点(checkpoint)。

DataNode将HDFS数据以文件的形式储存在本地的文件系统中,他并不知道有关HDFS文件的信息。它把每个HDFS数据块储存在本地文件系统的一个单独文件中。DataNode并不在同一个目录创建所有的文件,实际上,他用试探的方法来确定每个目录的最佳文件数目,并且在适当的时候创建子目录。在同一个目录中创建的所有本地文件并不是最优的选择,这是因为本地文件系统可能无法高效的在单个目录中支持大量的文件。


当一个DataNode启动时,他会扫描本地文件系统,产生一个这些本地文件对应的所有HDFS数据块的列表,然后作为报告发到NameNode,这个报告就是块状态报告

SecondaryNameNode

它不是NameNode的备份,但是可以作为NameNode的备份,当因为断电或服务器损坏的情况,可以用SecondaryNameNode中已合并的fsimage文件作为备份文件恢复到NameNode上,但是和有可能丢失掉在合并过程中新生成的edits信息,因此不死完全的备份。

由于NameNode仅在启动期间合并fsimage和edits文件,因此在繁忙的集群上,edits日志可能随着时间变得非常大。较大编辑文件的另一个副作用是下一次重新启动NameNode需要更长时间。SecondaryNameNode的主要功能是帮助NameNode合并edits和fsimage文件,从而减少NameNode启动时间。

SNN执行合并时机

  • 根据文件配置的时间间隔fs.checkpoint.period -> 默认1小时
  • dfs.namenode.checkpoint.txns 默认设置为一百万,也就是edits中的事务条数达到100万就会触发一次合并,即使未达到检查点期间。
  1. 首先生成一个名为edits.new的文件用于记录合并过程中产生的日志信息

  2. 当触发到某一时机时(时间间隔达到1小时或者edits中的事务条数达到100万)时SecondaryNameNode 将edits文件 与 fsimage文件从NameNode上读取到SecondaryNameNode上;

  3. 将edits文件和fsimage进行合并操作,合并成一个fsimage.ckpt 文件

  4. 将生成的合并后的文件fsimage.ckpt文件转移到NameNode上;

  5. 将fsimage.ckpt在NameNode上变成fsimage文件替换NameNode上原有的
    fsimage文件,并将edits.new文件上变成edits文件替换掉NameNode上原有的edits文件
    SNN在hadoop2.x及以上版本在非高可用状态时还存在,但是在hadoop2.x及以上版本高可用状态下SNN就不存在了,在hadoop2.x及以上版本在高可用状态下,处于standby状态的NameNode来做合并操作

DataNode

  1. 管理附加到他们运行节点的储存,并允许用户数据储存在文件中
  2. 在内部,文件 被分割成一个或多个Block,并且这些块被存储在一组DataNode中
  3. 负责提供来自文件系统客户端的读取和写入请求
  4. 执行块创建,删除
  5. 启动DN进程的时候回向NN汇报Block信息
  6. 通过向NameNode发送心跳保持与之联系(5秒一次),如果NameNode没有收到DataNode的心跳,则认为DataNode已经丢失,并复制其上的Block到其他其他DataNode上

HDFS储存单元(Block)

文件被切分成固定大小的数据块

  • 默认数据块大小为64MB(hadoop1.x)、128MB(hadoop2.x)、256MB(hadoop3.x),可配置;

  • 若文件大小不到一个块大小,则单独存成一个block,block块是一个逻辑意义上的概念。文件大小是多少,就占多少空间

一个文件储存方式

  • 按大小被切分成不同的block,存储到不同的节点上;

  • 默认情况下,每个block都有3个副本;

  • block大小与副本数通过client端上传文件时设置,文件上传成功后副本数可以变更,block size不可变更。

安全模式

  • NameNode在启动的时候会进入一个称为安全模式的特殊状态,它首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各项操作;
  • 一旦在内存中成功建立文件系统元数据映射,则创建一个新的fsimage文件(这个操作不需要SecondNameNode来做)与一个空的编辑日志;
  • 此刻namenode运行在安全模式,即namenode的文件系统对于客户端来说是只读的,显示目录、显示文件内容等,写、删除、重命名都会失败;
  • 在此阶段namenode搜集各个datanode的报告,当数据块达到最小副本数以上时,会被认为是“安全”的,在一定比例的数据块被认为是安全的以后(可设置),再过若干时间,安全模式结束;
  • 当检测到副本数不足数据块时,该块会被复制,直到达到最小副本数,系统中数据块的位置并不是由namenode维护的,而是以块列表形式存储在datanode中。