该文章是我通过Hadoop权威指南第4版对于HDFS的一些简单的归纳总结,语言不会特别官方,会更容易理解一点。欢迎大家观看!(如果有归纳出错的也欢迎大家指出。)
1.设计模式超大文件:可以存放超大文件
流式数据访问:一次写入,多次读取
商用硬件:不需要昂贵高可靠的硬件
低时间延迟的数据访问:要求低时间延迟的数据访问应用不适合运行在集群上,HDFS是为了超大数据高吞吐应用优化的
大量小文件:namenode会将元数据存储在内存中,因此HDFS存储的文件总数受限于namenode的内存总量
多用户写入,任意修改文件:HDFS文件只支持单个用户以“只添加”在文件末尾写入
2.1 数据块
HDFS与单一磁盘上的文件系统相似,也会有块(block)的概念,但是HDFS的块大的多,默认为128MB,HDFS文件会被分成为块大小的多个分块,每个块都是独立的存储单元,并且HDFS一个小于块大小的文件不会占据整个块的空间(例如:一个1MB的文件存储在一个128MB的块中时,文件只会使用1MB的磁盘空间,而不是128MB)。
HDFS的块为什么这么大?答:HDFS的块比磁盘的块大,是为了最小化寻址开销,如果块足够大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。因而,传输一个有多个块组成的大文件时间取决于磁盘的传输速率。默认的块大小为128M,随着磁盘驱动器传输速率的提升,块的大小会被设置的很大。但是也不会设置太大,因为MapReduce的map任务通常一次只处理一个块的数据,如果任务数太少,会导致作业的运行速度变慢
分布式文件系统对块抽象的好处:
一个文件的大小可以大于中任意一个磁盘的容量。文件的所有块不需要存储在同一个磁盘上,可以利用集群上任意一个磁盘进行存储。极端情况下也可以整个HDFS集群只存储一个大文件,这一个文件可以占满集群中的所有磁盘。
使用抽象块而非整个文件作为存储单元,大大简化了存储子系统的设计。由于块的大小是固定的,因此计算单个磁盘可以存储的块数量就相对容易,并且块可以只用来存储数据,文件的元数据,如权限信息,可以不与块一同存储,可以用其他系统单独管理这些元数据。
块还非常适合用于数据备份进而提升数据容错能力和提供可用性。可以将每个块复制到几个物理上相互独立的机器上(也就是我们常说的副本数,默认为3个),如果发现一个块不可用,可以从其他机器上读取副本块,确保不会因为块、磁盘、机器发生故障导致数据丢失。
与磁盘文件系统相似,HDFS中fsck指令可以显示查看块信息。
% hdfs fsck / -files -blocks
2.2 namenode 和 datanode
HDFS集群有两类节点:一个namenode(管理节点) 和多个datanode(工作节点)。
namenode:管理文件系统的命名空间,它维护着文件系统内所有的文件和目录。这些信息以文件的形式永久保存在本地磁盘上:命名空间镜像文件和编辑日志文件。namenode也记录着每个文件中块所在的数据节点的信息,但它不会永久保存块的位置信息,因为这些信息会在系统启动时根据数据节点信息重建。
什么是命名空间镜像文件和编辑日志文件?编辑日志文件(edits):文件系统客户端执行写 *** 作时(例如创建或移动文件),这些事务首先被记录到编辑日志中。namenode会在内存中维护文件系统的元数据,当发现编辑日志被修改时,相关的元数据信息也会同步更新。内存中的元数据信息可支持客户端的读请求。命名空间镜像文件(fsimage):就是文件系统元数据的一个完成的永久性检查点,fsimage文件中包含文件系统中所有的目录和文件的元数据的序列化信息。
datanode:文件系统的工作节点。它们根据需要存储并检索数据块,并且定期向namenode发送它们所存储的块的列表。
没有namenode的文件系统将无法使用。如果运行namenode服务的机器毁坏,文件系统的所有文件将会丢失,因为我们不知道如何根据datanode的块重建文件。因此,对namenode实现容错非常重要,Hadoop为次提供了两种机制。
一种是备份那些组成文件系统元数据持久状态的文件。Hadoop可以通过配置使namenode在多个文件系统上保存元数据的持久状态。这些写 *** 作是实时同步,并且是原子 *** 作。一般配置的是,将持久状态写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS)。
另一种运行一个辅助namenode,但它不能被用作namenode。这个辅助namnode的主要作用是定期合并编辑日志和命名空间镜像文件。并且保存合并后命名空间镜像的副本,在namenode发生故障启用。辅助namenode 保存的状态总是会滞后与主节点,所以在主节点失效后,难免会丢失部分数据。
通过这两种机制创建检测点能防止文件系统的所有文件丢失,但是依旧无法实现文件系统的高可用性,namenode依旧存在单点问题。
2.3 块缓存
datanode从磁盘读取块时,对于访问频繁的文件,其对应的块可能被显示地缓存在datanode的内存中,以堆外缓存(off-heap block cache)的形式存在,作业调度器通过在缓存块的datanode上运行任务,可以利用缓存的优势提高读 *** 作的性能。
2.4 联邦HDFS
namenode在内存中保存文件系统的每个文件和每个数据块的引用关系,这意味着对于一个拥有大量文件的超大集群来说,内存将成为限制系统横向扩展的瓶颈,所以在2.x 发行版本中引入联邦HDFS允许系统通过添加namenode实现扩展,其中每个namenode管理文件系统命名空间的一部分,例如,一个namenode可能管理/user目录下的所有文件,而另一个namenode可能管理/share目录下所有的文件。
在联邦环境下,每个namenode维护一个命名空间卷,由命名空间的元数据和一个数据块池组成,数据块池包含该命名空间下文件的所有数据块。命名空间卷之间相互独立,两两之间不互相通信,甚至其中一个namenode失效也不影响其他namenode维护的命名空间的可用性。数据块池不再进行切分,因此集群中的每个datanode需要注册到每个namenode,并且存储着来自多个数据块池的数据块。
2.5 HDFS的高可用性
通过联合使用在多个文件系统中备份namenode的元数据和通过备用namenode创建监测点能防止数据丢失,但是依旧无法实现文件系统的高可用。namenode依旧存在单点失效的问题。如果namenode失效了,那么所有的客户端,包括MapReduce作业,都将无法读、写或列举(list)文件,因为namenode是唯一存储元数据与文件到数据块映射的地方。
要想从一个失效的namenode恢复,首先需要启动一个拥有文件系统元数据的副本的新的namenode,并配置datanode和客户端以便使用新的namenode。新的namenode要满足以下情形才能响应服务:
将命名空间的映像导入到内存中
重演编辑日志
接收到足够多的来自datanode的数据块报告并退出安全模式。
对于一个拥有大量文件的和数据块的集群,namenode的冷启动需要很长的时间,所以Hadoop2.x 增加了对HDFS高可用性(HA)的支持,配置了活动(active)、备用(standby)两个namenode。当活动namenode失效后,备用namenode就会接管他的任务并开始服务于来自客户端的请求,不会有任何明显的中断。
namenode之间通过高可用共享存储实现编辑日志的共享。当备用namenode接管工作之后,它将通读共享编辑日志直至结尾,以实现与活动namenode的状态同步,并继续读取由活动namenode写入的条目。
datanode需要同时向两个namenode发送数据块处理报告,因为数据块的映射信息存储在namenode 的内存中,而非磁盘。
客户端需要使用特定的机制来处理namenode的失效问题,这一机制对用户是透明的。
辅助namenode的角色被备用namenode所包含,备用namenode为活动namenode命名空间设置周期性检查点。
如何实现高可用性共享存储:
NFS过滤器
群体日志管理器(QJM,quorum journal manager)。QJM是一个专用于HDFS实现的,为提供一个高可用的编辑日志而设计,QJM以一组日志节点(journal node)的形式运行,每一次编辑必须写入多数的日志节点。例如,有三个journal节点,系统可以忍受其中任意一个丢失。
故障切换与规避
系统中有一个成为故障转移控制器(failover controller)的新实体,管理着将活动namenode转移为备用namenode的转换过程。有多种故障转移控制器,默认的一种使用了Zookeeper来确保有且仅有一个活动的namenode。每一个namenode都会运行一个轻量级的故障转移控制器,它的工作就是通过心跳机制来监视宿主namenode是否失效,并在失效的时候进行故障切换。
管理员也可以手动触发故障转移,这被称为“平稳的故障转移”,因为故障转移器可以组织两个namenode有序的切换角色。
但在非故障转移的情况下,无法确切知道失效的namenode是否已经停止运行。例如,在网速非常慢的情况下也有可能激发故障转移,但是先前活动的namenode依然运行并且依旧是活动namenode。高可用实现了进一步优化,以确保先前活动的namenode不会执行危害系统并导致系统崩溃的 *** 作,该方法成为“规避”(fencing)。
同一时间QJM仅允许一个namenode向编辑日志写入数据,然而,对于先前活动的namenode,仍可能响应并处理客户过时的读请求,所以可以设置SSH规避命令用于杀死namenode进程。
客户端是通过客户端配置文件实现故障转移的控制。HDFS URI 使用一个逻辑主机名,该主机名映射到一对namenode地址(在配置文件中设置),客户端就会访问每一个namenode地址,直至处理完成。
3. 数据流3.1 剖析文件读取
HDFS读文件步骤:
客户端调用FileSystem对象的open()方法打开读取文件,对于HDFS来说,这个对象是DistributedFileSystem的一个实例。
DistributedFileSystem通过远程调用(RPC)来调用namenode,获取数据块的位置信息。
DistributedFileSystem类返回一个支持文件定位的输入流的对象,客户端对这个输入流调用read()方法。
输入流随即连接距离最近的文件中的第一个块所在的datanode。通过对数据流反复调用read()方法,将数据从datanode传输到客户端。
传完一个块的数据后,输入流关闭与该datanode的连接,接着寻找下一个块最佳的datanode。
一旦客户端读取完成,就对输入流调用close()方法结束读流程。
HDFS读数据流程图
tips:读取数据时,如果输入流与datanode通信时遇到错误时,会尝试从这个块另一个最邻近的datanode读取数据,并记录故障的datanode,以保证以后不会反复读取该节点上后续的块,数据流也会检验和确认从datanode发来的数据是否完整,如果发现有损坏的块,会试图从其他datanode读取副本,并将损坏的块通知给namenode。
3.2 剖析文件写入
HDFS写文件步骤:
客户端通过调用DistributedFileSystem对象的create()来创建文件。
DistributedFileSystem通过远程调用(RPC)来调用namenode,在系统命名空间新建一个文件,此时文件没有对应的数据块。
namenode会执行各种检查来确保这个文件不存在以及客户端有新建该文件的权限。如果检查通过,namenode就会为创建新文件记录一条记录,DistributedFileSystem类返回一个输出流(FSDataOutputStream)对象,并且客户端开始写入数据;若检查未通过,则文件新建失败并向客户端抛出一个IOException异常。
输出流将它们分成一个个的数据包,写入“数据队列”(data queue)。DataStreamer处理数据队列,挑选出最适合存储数据副本的一组datanode。假设复本数为3,那么会将这个3个datanode组成一个拥有3个节点的“管线”。DataStreamer将数据包流式传输到管线的第一个datanode,该datanode将存储数据包并且再传给管线的第二个datanode,同样,第二个datanode也会存储数据包并将它发给管线的第三个(也就是最后一个)datanode。
输出流也维护了一个“确认队列”(ack queue)来等待datanode的收到确认回执。收到管线中所有datanode确认信息后,数据写入完成。
客户端完成数据写入后,对数据流调用close()方法关闭数据流。
确认将所有的数据包写入完成后,告知namenode文件写入完成。
HDFS写数据流程图
tips:如果datanode在数据写入期间发生故障怎么处理?
首先关闭管线,确认把队列的数据包都添加到数据队列的最前端,以保证故障节点下有的datanode不会漏掉任何一个数据包。
为存储在另一个datanode的当前数据块指定一个新的标识,并将故障datanode恢复的时候可以删除存储的部分数据块。
从管线中删除故障的datanode,剩余的datanode构建成一条管线,继续完成写入,只要写入了dfs.namenode.replication.min(默认为1)的复本数,写 *** 作就会成功。
namenode会检查到块的副本量不足时,会在另一个节点创建新的复本。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)