本文主要从两个方面对hdfs进行阐述,第一就是hdfs的整个架构以及组成,第二就是hdfs文件的读写流程。

一、HDFS概述

标题中提到hdfs(Hadoop Distribute File System)是分布式文件系统

分布式文件系统 distributed file system 是指文件系统管理的物理存储资源不一定直接链接在本地节点上,而是通过计算机网络与节点相连,可让多机器上的多用户分享文件和存储空间。分布式文件系统的设计基于客户机/服务器模式

分布式文件系统的特点:
1、分布式文件系统可以有效解决数据的存储和管理难题
2、将固定于某个地点的某个文件系统,扩展到任意多个地点/多个文件系统
3、众多的节点组成一个文件系统网络
4、每个节点可以分布在不同的地点,通过网络进行节点间的通信和数据传输
5、在使用分布式文件系统时,无需关心数据是存储在哪个节点上、或者是从哪个节点获取的,只需要像使用本地文件系统一样管理和存储文件系统中的数据

Hadoop之(HDFS)是一种分布式文件系统,设计用于在商用硬件上运行。 它与现有的分布式文件系统有许多相似之处。 但是,与其他分布式文件系统的差异很大。
HDFS具有高度容错能力,旨在部署在低成本硬件上。
HDFS提供对应用程序数据的高吞吐量访问,适用于具有大型数据集的应用程序。
HDFS放宽了一些POSIX要求,以实现对文件系统数据的流式访问

HDFS优势:

1、可构建在廉价机器上,设备成本相对低
2、高容错性,HDFS将数据自动保存多个副本,副本丢失后,自动恢复,防止数据丢失或损坏
3、适合批处理,HDFS适合一次写入、多次查询(读取)的情况,适合在已有的数据进行多次分析,稳定性好
4、适合存储大文件,其中的大表示可以存储单个大文件,因为是分块存储,以及表示存储大量的数据

HDFS劣势:

1、由于提高吞吐量,降低实时性
2、由于每个文件都会在namenode中记录元数据,如果存储了大量的小文件,会对namenode造成很大的压力
3、不合适小文件处理,在mapreduce的过程中小文件的数量会造成map数量的增大,导致资源被占用,而且速度慢。
4、不适合文件的修改,文件只能追加在文件的末尾,不支持任意位置修改,不支持多个写入者操作

二、HDFS架构

hdfs架构图如下图所示:

HDFS具有主/从架构。HDFS集群由单个NameNode,和多个datanode构成。

NameNode:管理文件系统命名空间的主服务器和管理客户端对文件的访问组成,如打开,关闭和重命名文件和目录。负责管理文件目录、文件和block的对应关系以及block和datanode的对应关系,维护目录树,接管用户的请求。如下图所示:

1、将文件的元数据保存在一个文件目录树中
2、在磁盘上保存为:fsimage 和 edits
3、保存datanode的数据信息的文件,在系统启动的时候读入内存。

DataNode:(数据节点)管理连接到它们运行的​​节点的存储,负责处理来自文件系统客户端的读写请求。DataNodes还执行块创建,删除

Client:(客户端)代表用户通过与nameNode和datanode交互来访问整个文件系统,HDFS对外开放文件命名空间并允许用户数据以文件形式存储。用户通过客户端(Client)与HDFS进行通讯交互。

块和复制:
我们都知道linux操作系统中的磁盘的块的大小默认是512,而hadoop2.x版本中的块的大小默认为128M,那为什么hdfs中的存储块要设计这么大呢?
其目的是为了减小寻址的开销。只要块足够大,磁盘传输数据的时间必定会明显大于这个块的寻址时间。

那为什么要以块的形式存储文件,而不是整个文件呢?
1、因为一个文件可以特别大,可以大于有个磁盘的容量,所以以块的形式存储,可以用来存储无论大小怎样的文件。
2、简化存储系统的设计。因为块是固定的大小,计算磁盘的存储能力就容易多了
3、以块的形式存储不需要全部存在一个磁盘上,可以分布在各个文件系统的磁盘上,有利于复制和容错,数据本地化计算

块和复本在hdfs架构中分布如下图所示:

既然namenode管理着文件系统的命名空间,维护着文件系统树以及整颗树内的所有文件和目录,这些信息以文件的形式永远的保存在本地磁盘上,分别问命名空间镜像文件fsimage和编辑日志文件Edits。datanode是文件的工作节点,根据需要存储和检索数据块,并且定期的向namenode发送它们所存储的块的列表。那么就知道namenode是多么的重要,一旦那么namenode挂了,那整个分布式文件系统就不可以使用了,所以对于namenode的容错就显得尤为重要了,hadoop为此提供了两种容错机制

容错机制一:

就是通过对那些组成文件系统的元数据持久化,分别问命名空间镜像文件fsimage(文件系统的目录树)和编辑日志文件Edits(针对文件系统做的修改操作记录)。磁盘上的映像FsImage就是一个Checkpoint,一个里程碑式的基准点、同步点,有了一个Checkpoint之后,NameNode在相当长的时间内只是对内存中的目录映像操作,同时也对磁盘上的Edits操作,直到关机。下次开机的时候,NameNode要从磁盘上装载目录映像FSImage,那其实就是老的Checkpoint,也许就是上次开机后所保存的映像,而自从上次开机后直到关机为止对于文件系统的所有改变都记录在Edits文件中;将记录在Edits中的操作重演于上一次的映像,就得到这一次的新的映像,将其写回磁盘就是新的Checkpoint(也就是fsImage)。但是这样有很大一个缺点,如果Edits很大呢,开机后生成原始映像的过程也会很长,所以对其进行改进:每当 Edits长到一定程度,或者每隔一定的时间,就做一次Checkpoint,但是这样就会给namenode造成很大的负荷,会影响系统的性能。于是就有了SecondaryNameNode的需要,这相当于NameNode的助理,专替NameNode做Checkpoint。当然,SecondaryNameNode的负载相比之下是偏轻的。所以如果为NameNode配上了热备份,就可以让热备份兼职,而无须再有专职的SecondaryNameNode。所以架构图如下图所示:

SecondaryNameNode工作原理图:

SecondaryNameNode主要负责下载NameNode中的fsImage文件和Edits文件,并合并生成新的fsImage文件,并推送给NameNode,工作原理如下:

1、secondarynamenode请求主namenode停止使用edits文件,暂时将新的写操作记录到一个新的文件中;
2、secondarynamenode从主namenode获取fsimage和edits文件(通过http get)
3、secondarynamenode将fsimage文件载入内存,逐一执行edits文件中的操作,创建新的fsimage文件。
4、secondarynamenode将新的fsimage文件发送回主namenode(使用http post).
5、namenode用从secondarynamenode接收的fsimage文件替换旧的fsimage文件;用步骤1所产生的edits文件替换旧的edits文件。同时,还更新fstime文件来记录检查点执行时间。
6、最终,主namenode拥有最新的fsimage文件和一个更小的edits文件。当namenode处在安全模式时,管理员也可调用hadoop dfsadmin –saveNameSpace命令来创建检查点。

从上面的过程中我们清晰的看到secondarynamenode和主namenode拥有相近内存需求的原因(因为secondarynamenode也把fsimage文件载入内存)。因此,在大型集群中,secondarynamenode需要运行在一台专用机器上。

创建检查点的触发条件受两个配置参数控制。通常情况下,secondarynamenode每隔一小时(有fs.checkpoint.period属性设置)创建检查点;此外,当编辑日志的大小达到64MB(有fs.checkpoint.size属性设置)时,也会创建检查点。系统每隔五分钟检查一次编辑日志的大小。

容错机制二:

高可用方案(详情见:hadoop高可用安装和原理详解

三、HDFS读数据流程

HDFS读数据流程如下图所示:

1、客户端通过FileSystem对象(DistributedFileSystem)的open()方法来打开希望读取的文件。

2、DistributedFileSystem通过远程调用(RPC)来调用namenode,获取到每个文件的起止位置。对于每一个块,namenode返回该块副本的datanode。这些datanode会根据它们与客户端的距离(集群的网络拓扑结构)排序,如果客户端本身就是其中的一个datanode,那么就会在该datanode上读取数据。DistributedFileSystem远程调用后返回一个FSDataInputStream(支持文件定位的输入流)对象给客户端以便于读取数据,然后FSDataInputStream封装一个DFSInputStream对象。该对象管理datanode和namenode的IO。

3、客户端对这个输入流调用read()方法,存储着文件起始几个块的datanode地址的DFSInputStream随即连接距离最近的文件中第一个块所在的datanode,通过数据流反复调用read()方法,可以将数据从datanode传送到客户端。当读完这个块时,DFSInputStream关闭与该datanode的连接,然后寻址下一个位置最佳的datanode。

客户端从流中读取数据时,块是按照打开DFSInputStream与datanode新建连接的顺序读取的。它也需要询问namenode来检索下一批所需块的datanode的位置。一旦客户端完成读取,就对FSDataInputStream调用close()方法。

 注意:在读取数据的时候,如果DFSInputStream在与datanode通讯时遇到错误,它便会尝试从这个块的另外一个临近datanode读取数据。他也会记住那个故障datanode,以保证以后不会反复读取该节点上后续的块。DFSInputStream也会通过校验和确认从datanode发送来的数据是否完整。如果发现一个损坏的块, DFSInputStream就会在试图从其他datanode读取一个块的复本之前通知namenode。

   总结:在这个设计中,namenode会告知客户端每个块中最佳的datanode,并让客户端直接联系该datanode且检索数据。由于数据流分散在该集群中的所有datanode,所以这种设计会使HDFS可扩展到大量的并发客户端。同时,namenode仅需要响应位置的请求(这些信息存储在内存中,非常高效),而无需响应数据请求,否则随着客户端数量的增长,namenode很快会成为一个瓶颈。

四、HDFS写数据流程

HDFS写数据流程图如下图所示:

1、首先客户端通过DistributedFileSystem上的create()方法指明一个预创建的文件的文件名

2、DistributedFileSystem再通过RPC调用向NameNode申请创建一个新文件(这时该文件还没有分配相应的block)。namenode检查是否有同名文件存在以及用户是否有相应的创建权限,如果检查通过,namenode会为该文件创建一个新的记录,否则的话文件创建失败,客户端得到一个IOException异常。DistributedFileSystem返回一个FSDataOutputStream以供客户端写入数据,与FSDataInputStream类似,FSDataOutputStream封装了一个DFSOutputStream用于处理namenode与datanode之间的通信。

3、当客户端开始写数据时(,DFSOutputStream把写入的数据分成包(packet), 放入一个中间队列——数据队列(data queue)中去。DataStreamer从数据队列中取数据,同时向namenode申请一个新的block来存放它已经取得的数据。namenode选择一系列合适的datanode(个数由文件的replica数决定)构成一个管道线(pipeline),这里我们假设replica为3,所以管道线中就有三个datanode。

4、DataSteamer把数据流式的写入到管道线中的第一个datanode中,第一个datanode再把接收到的数据转到第二个datanode中,以此类推。

5、DFSOutputStream同时也维护着另一个中间队列——确认队列(ack queue),确认队列中的包只有在得到管道线中所有的datanode的确认以后才会被移出确认队列

如果某个datanode在写数据的时候当掉了,下面这些对用户透明的步骤会被执行:

管道线关闭,所有确认队列上的数据会被挪到数据队列的首部重新发送,这样可以确保管道线中当掉的datanode下流的datanode不会因为当掉的datanode而丢失数据包。

在还在正常运行的datanode上的当前block上做一个标志,这样当当掉的datanode重新启动以后namenode就会知道该datanode上哪个block是刚才当机时残留下的局部损坏block,从而可以把它删掉。

已经当掉的datanode从管道线中被移除,未写完的block的其他数据继续被写入到其他两个还在正常运行的datanode中去,namenode知道这个block还处在under-replicated状态(也即备份数不足的状态)下,然后他会安排一个新的replica从而达到要求的备份数,后续的block写入方法同前面正常时候一样。有可能管道线中的多个datanode当掉(虽然不太经常发生),但只要dfs.replication.min(默认为1)个replica被创建,我们就认为该创建成功了。剩余的replica会在以后异步创建以达到指定的replica数。

6、当客户端完成写数据后,它会调用close()方法。这个操作会冲洗(flush)所有剩下的package到pipeline中。

7、等待这些package确认成功,然后通知namenode写入文件成功。这时候namenode就知道该文件由哪些block组成(因为DataStreamer向namenode请求分配新block,namenode当然会知道它分配过哪些blcok给给定文件),它会等待最少的replica数被创建,然后成功返回。

注意:hdfs在写入的过程中,有一点与hdfs读取的时候非常相似,就是:DataStreamer在写入数据的时候,每写完一个datanode的数据块,都会重新向nameNode申请合适的datanode列表。这是为了保证系统中datanode数据存储的均衡性。

hdfs写入过程中,datanode管线的确认应答包并不是每写完一个datanode,就返回一个确认应答,而是一直写入,直到最后一个datanode写入完毕后,统一返回应答包。如果中间的一个datanode出现故障,那么返回的应答就是前面完好的datanode确认应答,和故障datanode的故障异常。这样我们也就可以理解,在写入数据的过程中,为什么数据包的校验是在最后一个datanode完成。

更多hadoop生态文章见:hadoop生态系列

参考:

《Hadoop权威指南 大数据的存储与分析 第四版》

https://hadoop.apache.org/docs/r2.7.7/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html

hadoop之hdfs架构详解的更多相关文章

  1. hadoop之hdfs命令详解

    本篇主要对hadoop命令和hdfs命令进行阐述,yarn命令会在之后的文章中体现 hadoop fs命令可以用于其他文件系统,不止是hdfs文件系统内,也就是说该命令的使用范围更广可以用于HDFS. ...

  2. hdfs架构详解(防脑裂fencing机制值得学习)

    HDFS(Hadoop Distributed File System)是一个分布式文件存储系统,几乎是离线存储领域的标准解决方案(有能力自研的大厂列外),业内应用非常广泛.近段抽时间,看一下 HDF ...

  3. HDFS架构详解-非官档

    Namenode 1.namenode是一个中心服务器,单一节点(简化系统的设计和实现),负责管理文件系统的命名空间(namespace)以及客户端对文件的访问. 2.文件操作:namenode负责对 ...

  4. hadoop(10)---hdfs配置文件详解

    以下只是简单的对hdfs(hdfs.site.xml)配置文件做一个简单的说明. <configuration><property><!-- 为namenode集群定义一 ...

  5. hdfs文件系统架构详解

    hdfs文件系统架构详解 官方hdfs分布式介绍 NameNode *Namenode负责文件系统的namespace以及客户端文件访问 *NameNode负责文件元数据操作,DataNode负责文件 ...

  6. hadoop hdfs uri详解

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  7. hadoop应用开发技术详解

    <大 数据技术丛书:Hadoop应用开发技术详解>共12章.第1-2章详细地介绍了Hadoop的生态系统.关键技术以及安装和配置:第3章是 MapReduce的使用入门,让读者了解整个开发 ...

  8. 《Hadoop应用开发技术详解》

    <Hadoop应用开发技术详解> 基本信息 作者: 刘刚 丛书名: 大数据技术丛书 出版社:机械工业出版社 ISBN:9787111452447 上架时间:2014-1-10 出版日期:2 ...

  9. Hadoop Hive sql语法详解

    Hadoop Hive sql语法详解 Hive 是基于Hadoop 构建的一套数据仓库分析系统,它提供了丰富的SQL查询方式来分析存储在Hadoop 分布式文件系统中的数据,可以将结构 化的数据文件 ...

随机推荐

  1. 基于注解的SpringAOP源码解析(三)

    注意,读完本篇文章需要很长很长时间 在之前的2篇文章:AOP源码分析(一)AOP源码分析(二) 中,我们搭建了SpringAOP源码分析的环境,介绍了@EnableAspectJAutoProxy注解 ...

  2. Docker:镜像的迁移

    从202将现有镜像搬到207的过程. 先说导出,两种方法:Docker save 和 docker export,前者保存镜像,后者导出容器. docker save docker.io/java:7 ...

  3. .Net使用HttpClient以multipart/form-data形式post上传文件及其相关参数

    前言: 本次要讲的是使用.Net HttpClient拼接multipark/form-data形式post上传文件和相关参数,并接收到上传文件成功后返回过来的结果(图片地址,和是否成功).可能有很多 ...

  4. python 39 socketserver 模块并发通信

    socketserver模块 socketserver模块实现一个服务端与多个客户端通信.是在socket的基础上进行了一层封装,底层还是调用的socket. socketserver干了两件事: 1 ...

  5. jqGrid根据数据动态设置rowList

    superme.rowList = [10,20,50,100]; superme.rowNum = 20; 全局属性 loadComplete : function (data) { if(data ...

  6. 使用JDBC驱动程序处理元数据

    使用 JDBC 驱动程序处理元数据 一.前言 Java 通过JDBC获得连接以后,得到一个Connection 对象,可以从这个对象获得有关数据库管理系统的各种信息,包括数据库中的各个表,表中的各个列 ...

  7. .gitignore不起作用,过滤规则

    git 通过配置.gitignore文件忽略掉的文件或目录,在.gitignore文件中的每一行保存一个匹配的规则 # 此为注释 – 将被 Git 忽略 *.a :忽略所有 .a 结尾的文件 !lib ...

  8. Centos安装和配置Mysql5.7

    [root@localhost ~]# wget https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm -bash ...

  9. Python 命令行之旅:使用 argparse 实现 git 命令

    作者:HelloGitHub-Prodesire HelloGitHub 的<讲解开源项目>系列,项目地址:https://github.com/HelloGitHub-Team/Arti ...

  10. codeforces 633F The Chocolate Spree (树形dp)

    题目链接:http://codeforces.com/problemset/problem/633/F 题解:看起来很像是树形dp其实就是单纯的树上递归,就是挺难想到的. 显然要求最优解肯定是取最大的 ...