HDFS原理解析

本文详细介绍了HDFS的设计基础与目标,包括其超大文件处理、流式访问数据和商用硬件支持。接着,深入探讨了HDFS的体系结构,如Namenode、SecondaryNamenode和Datanode的职责和目录结构。重点讨论了HDFS的高可靠性策略,如冗余副本、机架策略、心跳机制、安全模式和校验和等。此外,还涵盖了高可用性实现,包括故障切换和脑裂问题的避免。最后,提到了HDFS的异构层级存储结构和访问控制列表(ACL)的管理。HDFS通过这些机制确保大规模数据的安全存储和高效访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      HDFS提供分布式存储机制,提供可线性增长的海量存储能力。自动的数据冗余,无须使用Raid(磁盘阵列),无须另行备份。为进一步分析计算提供数据基础。

1 HDFS设计基础与目标

      (1)超大文件。HDFS能够处理百万规模以上的文件数量( GB、TB、PB级数据),能够处理10K节点的规模。
      (2)流式访问数据。一次写入,多次读取。Hadoop擅长做的是数据分析而不是事务处理。每次分析都涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。(由于数据集的存储是分布式的,故数据集文件不一定是顺序存放在一台机器上。在分析数据时,定位第一条记录对以后的读取并不会起太大的作用。多次读取,多次定位。我们需要做的是减少总的读取数据延迟)
      (3)商用硬件。Hadoop不需要运行在昂贵且高可靠的硬件上。由于庞大集群的硬件出错率比较高,故需要数据冗余。
      (4)HDFS是为高数据吞吐量应用优化的,这可能会以高时间延迟为代价。
      (5)大量的小文件不适合存储在HDFS中。每个文件都需要在Namenode的内存中进行目录映射,小文件和大文件占用Namenode的内存是一样的,如果出现大量的小文件,则会占用Namenode大量的内存,造成Namenode内存资源的浪费。
      (6)文件一旦写入不能修改,只能追加,从而保证数据的一致性。并且,HDFS的文件写入只支持单个写入者。

2 HDFS体系结构

      客户端代表用户通过与Namenode和Datanode交互来访问整个文件系统。

2.1 Namenode

      Namenode管理文件系统的命名空间。它维护元文件系统树及整棵树内所有的文件和目录。这些信息称为元数据,存放在Namenode的内存中,为了防止Namenode宕机之后无法在内存中重建元数据,所以将这些信息永久保存在fsimage文件和edits日志文件以作为备份。没有Namenode,文件系统无法使用。因此,对Namenode实现容错非常重要,Hadoop为此提供两种机制。第一种机制是备份那些组成文件系统元数据持久状态的文件。另一种可行的方法是运行一个辅助Namendoe。

2.1.1 Namenode的目录结构 在这里插入图片描述

(1)VERSION文件是Java属性文件,其中包含正在运行的HDFS版本信息。
      属性layoutVersion是一个负整数,描述HDFS持久性数据结构(也称布局)的版本,但是该版本号与Hadoop发布包的版本号无关。只要布局变更,版本号便会递减(例如,版本号-57之后是-58),此时HDFS也需要升级。
      属性namespaceID是文件系统命名空间的唯一标识符,是在Namenode首次格式化时创建的。
      属性clusterID是将HDFS集群作为一个整体赋予的唯一标识符,对于联邦HDFS非常重要,这里一个集群有多个命名空间组成,且每个命名空间由一个Namenode管理。
      属性blockpoolID是数据块池的唯一标识符,数据块池中包含了由一个Namenode管理的命名空间中的所有文件。
      属性cTime标记了Namenode存储系统的创建时间。对于刚刚格式化的存储系统,这个值为0;但是在文件系统升级之后,该值更新到新的时间戳。
      属性storageType说明该存储目录包含的是Namenode的数据结构。
(2)in_use.lock文件一个锁文件,Namenode使用该文件为存储目录加锁。
(3)edits日志文件
      文件系统客户端执行写操作时(例如创建或移动文件),这些事务首先记录到编辑日志中。Namendoe在内存中维护文件系统的元数据;当edits日志被修改时,相关元数据信息也同步更新。edits日志在概念上是单个实体,但是它体现为磁盘上的多个文件。每个文件成为一个“段”(segment),名称由前缀edits及后缀组成,后缀指示出该文件所包含的事务ID。任一时刻都只有一个文件处于打开写状态(前述例子中为edits_inprogress_ 0000000000000000020),在每个事务完成之后,且在向客户端发送成功代码之前,文件都需要更新和同步。当Namenode向多个目录写数据时,只有在所有写操作更新并同步到每个文件复本之后方可返回成功代码,以确保任何事务都不会因为机器故障而丢失。
(4)fsimage文件
      每个fsimage文件包含文件系统中的所有目录和文件inode的序列化信息。每个inode是一个文件或目录的元数据的内部描述。对于文件来说,包含的信息有"复本级别" (replication level) 、修改时间和访问时间、访问许可、块大小、组成一个文件的块等;对于目录来说,包含的信息有修改时间、访问许可和配额元数据等信息。
      fsimage也是文件系统元数据的一个完整的永久检查点,并非每一个写操作都会更新文件,因为fsimage是一个大型文件(甚至可高达几个GB),如果频繁地执行写操作,会使系统运行极为缓慢。这个特性不会降低系统的恢复能力,因为如果Namenode发生故障,最近的fsimage文件将被载入到内存以重构元数据的最近状态,再从相关点开始向前执行edits日志中记录的每个事务。
      数据块存储在Datanode中,但fsimage文件并不描述Datanode。Namenode将这种块映射关系放在内存中。当Datanode加入集群时,Namenode向Datanode索取块列表以建立块映射关系;Namenode还定期征询Datanode以确保它拥有最新的块映射。
(5)seen_txid文件
      seen_txid文件存放transactionId。format之后是0,它代表Namenode里面的edits_文件的尾数,Namenode重启的时候,会按照seen_txid的数字,循序从头跑edits_0000001~到seen_txid的数字。所以当你的HDFS发生异常重启的时候,一定要比对seen_txid内的数字是不是你edits最后的尾数(就是edits_inprogress_000000000后边的那串数字,inprogress就是正在使用的edits文件),不然会发生重新构建Namenode时元数据的资料有缺少,导致误删Datanode上多余块的信息。

2.2 SecondaryNamenode(辅助Namenode)

      如上所述,edits日志会无限增长(即使物理上它是分布在多个edits文件中)。尽管这种情况对于Namenode的运行没有影响,但由于需要恢复(非常长的)edits日志中的各项事务,Namenode的重启操作会比较慢。这段时间内,文件系统将处于离线状态,这会有违用户的期望。
      解决方案式运行辅助Namenode,为主Namenode内存中的文件系统元数据创建检查点。创建检查点过程如下: 在这里插入图片描述
      (1)辅助Namenode请求主Namenode停止使用正在进行中的edits文件,这样新的edits操作记录到一个新文件中。主Namenode还会更新所有存储目录中的seen_txid文件。
      (2)辅助Namenode从主Namenode获取最近的fsimage和edits文件(采用http get)。
      (3)辅助Namenode将fsimage文件载入内存,逐一执行edits文件中的事务,创建新的合并之后的fsimage文件。
      (4) 辅助Namenode将新的fsimage文件发送回主Namenode(http put),主Namenode将其保存为临时的.ckpt文件。
      (5)主Namenode重新命名临时的fsimage文件,便于日后使用。最后,主Namenode拥有最新的fsimage文件和一个更小的正在进行中edits文件(edits文件可能为空,因为在创建检查点过程中主Namenode还可能收到一些编辑请求)。
      检查点触发:①每隔一个周期执行一次。②即使不满足周期性,edits日志的大小到达一个阈值,也会创建检查点。由于辅助Namenode也把fsimage文件载入内存,故主Namenode和辅助Namenode的内存需求相近。因此,在大型集群中,辅助Namenode需要运行在一台专用机器上。

2.2.1 辅助Namenode的目录结构

      辅助Namenode的检查点目录的布局和主Namenode的检查点目录的布局相同。这种设计方案的好处是,在主Namendoe发生故障时(假设没有及时备份,甚至在NFS上也没有)可以从辅助Namenode恢复数据。方法一是将相关存储目录赋值到新的Namenode中;方法二是使用-importCheckpoint选项启动Namenode守护进程,从而将辅助Namenode用作新的主Namenode。只有当主Namenode的目录中没有元数据时,辅助Namenode才会从它的目录中载入最新的检查点Namenode元数据,因此,不必担心这个操作会覆盖现有的元数据。

2.3 Datanode

      Datanode是文件系统的工作节点。它们根据需要存储并检索数据块(受客户端或Namenode调度),并且定期向Namenode发送他们所存储的块的列表。通常Datanode从磁盘中读取块,但对于访问频繁的文件,其对应的块可能被显式地缓存在Datanode的内存中,以堆外块缓存(off-heap block cache)的形式存在。(插播:为了缓解在高并发,高写入操作下,堆内缓存组件造成的频繁GC问题,堆外缓存应运而生。因为堆内缓存是受JVM管控的,所以我们不必担心垃圾回收的问题。然而,堆外缓存是不受JVM管控的,所以它不受GC的影响导致的应用暂停问题。但是由于堆外缓存是以byte数组来进行的,所以需要自己进行序列化反序列化操作。)用户或应用通过在缓存池(cache pool) 中增加一个cache directive 来告诉Namenode需要缓存哪些文件及存多久。缓存池是一个用于管理缓存权限和资源使用的管理性分组。
      提到好几次块这个词了,这里对块进行说明一下:block数据块是HDFS文件系统基本的存储单位。block(块)默认大小为128M。小于一个块的文件,不会占据整个块的空间。block数据块大小设置较大的原因:①减少文件寻址时间②减少管理块的数据开销,每个块都需要在NameNode上有对应的记录③对数据块进行读写,减少建立网络的连接成本。

2.3.1 Datanode的目录结构

      和Namenode不同的是,Datanode的存储目录是初始阶段自动创建的,不需要额外格式化。Namenode格式化:文件系统在网络磁盘中还不存在,格式化实际上是在Namenode上开辟一块儿命名空间。这样HDFS就可以挂载在这个命名空间下。 在这里插入图片描述
      HDFS数据块存储在以blk_为前缀名的文件中,文件名包含了该文件存储的块的原始字节数。每个块有一个相关联的带有.meta后缀的元数据文件。元数据文件包括头部(含版本和类型信息)和该块各区段的一系列的校验和。
      每个块属于一个数据块池,每个数据块池都有自己的存储目录,目录根据数据块池ID形成(和Namenode的VERSION文件中的数据块池ID相同)。
      当目录中数据块的数量增加到一定规模时,Datanode会创建一个子目录来存放新的数据块及其元数据信息。终极目标是设计一棵高扇出的目录树,及时文件系统中的块数量非常多,目录树的层数也不多。Datanode通过这种方式可以有效地管理各个目录中的文件,避免了大多数操作系统遇到的管理难题,即很多(成千上万个)文件放在同一个目录中。
      如果dfs.datanode.data.dir属性制定了不同磁盘上的多个目录,那么数据块会以轮转的方式写到各个目录中。同一个Datanode上的每个磁盘上的块不会重复,只有不同Datanode之间的块才有可能重复。

2.4 读取数据流程

      客户端要访问HDFS中的文件,首先从Namenode获得组成这个文件的数据块位置列表,根据列表知道存储数据块的Datanode。访问Datanode获取数据。需要注意的是Namenode并不参与数据实际传输。这种设计使得HDFS可以扩展大量的并发客户端。
在这里插入图片描述
      (1)客户端通过FileSystem对象的open()方法来打开希望读取的文件,对于HDFS来说,这个对象是DistributedFileSystem的一个实例。
      (2)DistributedFileSystem通过使用远程过程调用RPC来调用Namenode,来确定文件起始块的位置。对于每一个块,Namenode返回存有该块副本的Datanode地址。这些Datanode根据它们与客户端的距离(两个节点之间的带宽作为距离的衡量标准)来排序。DistributedFileSystem返回一个FSDataInputStream对象(支持文件定位的输入流)给客户端一遍读取数据。FSDataInputStream转而封装成DFSInputStream对象,该对象管理着Datanode和Namenode的I/O。
      (3)客户端对这个输入流调用read()方法。存储着文件起始几个块Datanode地址的DFSInputStream随即连接距离最近的文件中第一个块所在的Datanode。
      (4)通过对数据流反复调用read()方法,可以将数据从Datanode传输到客户端。
      (5)到达块的末端时,DFSInputStream关闭与该Datanode的连接,然后寻找下一个块的最佳Datanode。
      :①如果DFSInputStream与Datanode通信时遇到错误,会尝试从这个块的另外一个最邻近Datanode读取数据。它也记住那个故障Datanode,以保证以后不会反复读取该节点上后续的块。DFSInputStream也会通过校验和确认从Datanode发来的数据是否完整。如果发现有损坏的块,DFSInputStream会试图从其他的Datanode读取副本,也会将损坏的块通知给Namenode。②Hadoop衡量节点之间的带宽方法:把网络看做一棵,两个节点之间的距离是它们到最近共同祖先的距离总和。该树中的层次是没有预先设定的,但是相对于数据中心,机架和正在运行的节点,通常可以设定等级。可用带宽依次递减:同一节点上的进程、同一机架上的不同节点、同一数据中心中不同机架上的节点和不同数据中心中的节点。
在这里插入图片描述
在这里插入图片描述

2.5 写入数据流程

      首先,客户端请求Namenode创建新文件。然后,客户端将数据写入DFSOutputStream。最后,建立pipeline依次将目标数据块写入各个Datanode,建立多个副本。 在这里插入图片描述
      (1)客户端通过对DistributedFileSystem对象调用create()来新建文件。
      (2)DistributedFileSystem对Namenode创建一个RPC调用,在文件系统的命名空间中新建一个文件,此时该文件中还没有相应的数据块。Namenode执行各种不同的检查以确保这个文件不存在以及客户端有新建该文件的权限。如果这些检查均通过,Namenode就会为创建新文件记录一条记录;否则,文件创建失败并向客户端抛出一个IOException异常。DistributedFileSystem向客户端返回一个FSDataOutputStream对象,客户端通过这个对象可以开始写入数据。就像读取事件一样,将FSDataOutputStream封装一个DFSOutputStream对象,该对象负责Datanode和Namenode之间的通信。
      (3)客户端写入数据时,DFSOutputStream将数据分成一个个的数据包,并写入内部队列,称为“数据队列”。
      (4)DataStreamer处理数据队列,它的责任是挑选出适合存储数据副本的一组Datanode,并以此来要求Namenode分配新的数据块。这一组Datanode构成一个管线(这里我们假设副本数为3)。DataStreamer将数据包流式传输到管线中第1个Datanode,该Datanode存储数据包并将它发送到管线中的第2个Datanode。同样,第2个Datanode存储该数据包并发送给管线中的第3个Datanode。
      (5)DFSOutputStream也维护着一个内部数据包队列来等待Datanode收到确认回执,称为“确认队列”(ack queue)。收到管道中所有Datanode确认信息后,该数据包才会从确认队列删除。
      (6)客户端完成写入后,读数据流电泳close()方法。该操作将剩余的所有数据包写入DataNode管道。
      (7)在联系到Namenode告知其文件写入完成之前,等待确认。
      :任何Datanode在数据写入期间发生故障,则执行以下操作(对写入数据的客户端是透明的)。首先关闭管道,确认把队列中的所有数据包都添加回数据队列的最前端,以确保故障节点下游的Datanode不会漏掉任何一个数据包。为存储在另一正常Datanode的当前数据制定一个新的标识,并将该标识传送给Namenode,以便故障Datanode在恢复后可以删除存储的部分数据块。从管道中删除故障Datanode,基于两个正常Datanode构建一条新管道。余下的数据块写入管道中正常的Datanode。Namenode注意到块副本量不足时,会在另一个节点上创建一个新的副本。

3 HDFS的高可靠性

3.1 冗余副本策略

      可以在hdfs-site.xml中设置复制因子(dfs.replication)指定副本数量。所有的数据都有副本。Datanode启动时,遍历本地文件系统,产生一份HDFS数据块和本地文件的对应关系列表(block report)汇报给Namenode。

3.2 机架策略(副本放置策略)

      Hadoop默认的布局策略是在运行客户端的节点上放第1个副本(如果客户端运行在集群之外,就随机选择一个节点,不过系统会避免挑选那些存储太满或太忙的节点)。第2个副本放在与第一个不同且随机另外选择的机架中的节点上。其他副本放在集群中随机选择的节点上,不过系统会尽量避免在用一个机架上放太多副本。
在这里插入图片描述
      总的来说,这一方法不仅提供很好的稳定性(数据块存储在两个机架中)并实现很好的负载均衡,包括写入带宽(写入操作只需要遍历一个交换机)、读取性能(可以从两个机架中选择读取)和集群中块的均匀分布(客户端只在本地机架上写入一个块)。

3.3 负载均衡

      当HDFS负载不均衡时,需要对HDFS进行数据的负载均衡调整,即对各节点机器上数据的存储分布进行调整。从而,让数据均匀的分布在各个DataNode上,均衡IO性能,防止热点的发生。进行数据的负载均衡调整,必须要满足如下原则:①数据平衡不能导致数据块减少,数据块备份丢失;②管理员可以中止数据平衡进程;③每次移动的数据量以及占用的网络资源,必须是可控的;④数据均衡过程,不能影响namenode的正常工作。负载均衡原理如下:在这里插入图片描述
      (1)数据均衡服务(Rebalancing Server)首先要求 NameNode 生成 DataNode 数据分布分析报告,获取每个DataNode磁盘使用情况;
      (2)Rebalancing Server汇总需要移动的数据分布情况,计算具体数据块迁移路线图。数据块迁移路线图,确保网络内最短路径;
      (3)开始数据块迁移任务,Proxy Source Data Node复制一块需要移动数据块;
      (4)将复制的数据块复制到目标DataNode上;
      (5)删除原始数据块;
      (6)目标DataNode向Proxy Source Data Node确认该数据块迁移完成。
      (7)Proxy Source Data Node向Rebalancing Server确认本次数据块迁移完成。然后继续执行这个过程,直至集群达到数据均衡标准。
      在第2步中,HDFS会把当前的DataNode节点,根据阈值的设定情况划分到Over、Above、Below、Under四个组中。在移动数据块的时候,Over组、Above组中的块向Below组、Under组移动。四个组定义如下: 在这里插入图片描述
      Over组:此组中的DataNode的均满足

 DataNode_usedSpace_percent > Cluster_usedSpace_percent + threshold

      Above组:此组中的DataNode的均满足

 Cluster_usedSpace_percent + threshold > DataNode_ usedSpace _percent >
  Cluster_usedSpace_percent。

      Below组:此组中的DataNode的均满足

 Cluster_usedSpace_percent > DataNode_ usedSpace_percent >
  Cluster_ usedSpace_percent – threshold

      Under组:此组中的DataNode的均满足

 Cluster_usedSpace_percent – threshold > DataNode_usedSpace_percent

      启动数据均衡,默认阈值为 10% start-balancer.sh
      启动数据均衡,阈值 5% start-balancer.sh –threshold 5
      停止数据均衡 stop-balancer.sh

3.4 心跳策略

      Namenode周期性从Datanode接收心跳信号和块报告,并根据块报告验证元数据。没有按时发送心跳的Datanode会被标记为宕机,不会再给它任何I/O请求。如果Datanode失效造成副本数量下降,并且低于预先设置的阈值,Namendoe会检测出这些数据块,并在合适的时机进行重新复制。引发重新复制的原因还包括数据副本本身损坏、磁盘错误和复制因子被增大等。

3.5 安全模式

      Namenode启动时会先经过一个“安全模式”阶段。安全模式阶段不会产生数据写。在此阶段Namenode收集各个Datanode的报告,当数据块达到最小副本数以上时,会被认为是“安全”的。在一定比例(可设置)的数据块被认定为“安全”后,再经过若干时间,安全模式结束。当检测到副本数不足的数据块时,该块会被复制直至达到最小副本数。
      进入安全模式:hadoop fs -safemode enter
      查看安全模式状态 hadoop fs -safemode get
      离开安全模式 hadoop fs -safemode leave

3.6 校验和

      在文件创建时,每个数据块都产生校验和,校验和保存在.meta文件中。检测数据是否损坏的常见措施是,在数据第一次引入系统时计算校验和(checksum)并存储,在数据进行传输后再次计算校验和进行对比,如果计算所得的新校验和和原来的校验和不匹配,就认为数据已损坏。但该技术并不能修复数据——它只能检测出数据错误。(这正是不使用低端硬件的原因。具体说来,一定要使用ECC内存。)注意,校验和也是可能损坏的,不只是数据,但由于校验和比数据小得多,所以损坏的可能性非常小。

3.6.1 对本地文件I/O的检查

      在Hadoop中,本地文件系统的数据完整性由客户端负责。重点在于存车读取文件时进行校验和的处理。 具体做法是:每当hadoop创建文件a时,hadoop就会同时在同一个文件夹下创建隐藏文件.a.crc,这个文件记录了 文件a的校验和。针对数据文件的大小,每512个字节会生成一个32位的校验和(4字节),可以在src/core/core-default.xml中通过修改io.bytes.per.checksum的大小来修改每个校验和所针对的文件的大小。

3.6.2 对HDFS的I/O数据进行检查

(1)DataNode接收数据后存储数据前
      DataNode接收数据一般有两种情况:①从客户端上传数据②DataNode从其他DataNode上接收数据。当客户端上传数据时,正在写数据的客户端将数据及其校验和发送到由一系列Datanode组成的Pipeline管道。Pipeline管线中最后一个Datanode负责验证校验和。
      DataNode数据存储步骤:(包括从DataNode和客户端两种传输方式)
      ① 在传输数据的最开始阶段,Hadoop会简单地检查数据块的完整性信息;
      ② 依次向各个DataNode传输数据,包括数据头信息、块信息、备份个数、校验和等;
      ③ Hadoop不会在数据每流动到一个DataNode都检查校验和,只会在数据流达到最后一个节点时才检查校验和。如果在验证过程中发现有不一致的块,就会抛出CheckSumException异常信息。
      hdfs会为每一个固定长度的数据(一个个数据包)执行一次校验和。这个值由io.bytes.per.checksum指定,默认是512字节。因为CRC32是32位即4个字节,这样校验和占用的空间就会少于原数据的1%。
(2)客户端读取DataNode上的数据时
      Hadoop会在客户端读取DataNode上的数据时,使用DFSClient中的read函数先将数据读入到用户的数据缓冲区,然后再检查校验和。将他们与Datanode中存储的校验和进行比较。每个Datanode均持久保存有一个用于验证的校验和日志,所以它知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会告诉这个Datanode,Datanode由此更新日志。
(3)DataNode后台守护进程的定期检查
      DataNode会在后台运行DataBlockScanner,这个程序定期验证存储在这个datanode上的所有数据块(3周)。该项措施是解决物理存储媒体上位衰减,位损坏的有力措施。

3.6.3 Hadoop处理损坏数据的机制

      (1)DataNode在读取block块的时候会先进行checksum(数据块校验和)。如果client发现本次计算的校验和跟创建时的校验和不一致,则认为该block块已损坏。
      (2)客户端在抛出ChecksumException之前上报该block信息给namenode进行标记(“已损坏”)。这样namenode就不会把客户端指向这个block,也不会复制这个block到其它的datanode。
      (3)client重新读取另外的datanode上的block。
      (4)在心跳返回时NameNode将块的复制任务交给DataNode,从完好的block副本进行复制以达到默认的备份数阈值。
      (5)NameNode删除掉坏的block。
      (6)DataNode在一个block块被创建之日起三周后开始进行校验

3.7 回收站

      删除文件时,其实是将删除的文件放入回收站.Trash目录下。回收站里的文件可以快速恢复。也可以设置一个时间阈值,当回收站里文件的存放时间超过这个阈值(默认是360分钟),就被彻底删除,并且释放占用的数据块。相关配置需要在core-site.xml文件进行配置。fs.trash.interval设置时间阈值。

3.8 元数据保护

      第一种机制是备份那些组成文件系统元数据持久状态的文件。Hadoop可以通过配置使Namenode在多个文件系统上保存元数据的持久状态。这些写操作都是实时同步的,且是原子操作。一般将持久状态写入本地磁盘的同时,写入远程挂载的网络文件系统(NFS)。副本的增多会将低Namenode的处理速度,但增加安全性。
      第二种机制是:运行一个辅助Namenode,但它不能被用作Namenode。这个辅助Namenode 的重要作用是定期合并edits日志与fsimage,以防止edits日志过大。这个辅助Namendoe一般在另一台单独的物理计算机上运行,因为它需要占用大量的CPU时间,并且需要与Namendoe一样多的内存来执行合并操作。它会保存合并后的fsimage的副本,并在Namenode发生故障时启用。但是,辅助Namendoe保存的状态总是滞后于主Namenode,所以主Namenode节点全部失效时,难免会丢失部分数据。在这种情况下, 一般把存储在NFS 上的Namenode 元数据复制到辅助Namenode 并作为新的主Namenode 运行。

3.9 快照机制

      HDFS允许管理者和用户对文件系统进行快照。快照是对文件系统子树在给定时刻的一个只读复本。由于并不真正复制数据,因此快照非常高效,它们简单地记录每个文件的元数据和块列表,这对于重构快照时刻的文件系统内容已经足够了。快照不是数据备份的替代品,但是对于恢复用户误删文件在特定时间点的数据而言,它们是一个有用的工具。当然,制定一个周期性快照策略。

3.10 联邦

      Namenode在内存中保存文件系统中每个文件和每个数据块的引用关系,这意味着对于一个拥有大量文件的超大集群来说,内存将成为限制系统横向扩展的瓶颈。因此,联邦机制出现了。在联邦环境下,每个Namenode维护一个命名空间卷,由命名空间的元数据和数据块池组成,数据块池包含该命名空间下文件的所有数据块。命名空间卷之间是相互独立的,两两之间并不相互通信,甚至其中一个Namenode的失效也不会影响由其他Namenode维护的命名空间的可用性。数据块池不再进行切分,因此集群中的Datanode需要注册到每个Namenode,并且存储着来自多个数据块池中的数据块。
      块池是属于单个命名空间的一组块。Datanode是一个物理概念,而block pool是一个重新将block划分的逻辑概念。同一个Datanode中可以存着属于多个block pool的多个块。block pool允许一个命名空间在丌通知其他命名空间的情况下为一个新的block创建block ID。 在这里插入图片描述
      不采用文件名hash这一在分布式系统里常用的手段,因为使用hash方法使得同一目录下的文件可能散布于各个命名空间,这样一旦文件出现问题,则会出现各个命名空间都出现部分文件不可读的情况,这种做法性能会变得很差。所以HDFS采用的是直接映射目录为命名空间,某种文件就只会在某个目录下,当这个文件出现问题时,其他的目录还能继续运作。

3.11 HDFS的高可用
3.11.1 HA机制简介

      通过联合使用在多个文件系统中备份Namenode的元数据和通过辅助Namenode创建检查点能防止数据丢失,但是依旧无法实现文件系统的高可用。Namenode依然存在单点失效的问题。新的Namenode冷启动需要30分钟,甚至更长时间。启动过程:①将命名空间的映像文件导入内存;②重演edits日志;③接收到足够多的来自Datanode的数据块报告并退出安全模式。因此,HDFS的高可用出现了。在这一实现中,配置了一对活动-备用(active-standby)Namendoe。当活动的Namenode失效,备用Namenode就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显中断(几十秒或者1分钟,这是因为系统需要确定活动的Namenode是否真的失效了)。
      (1)Namenode之间需要通过高可用共享存储实现edits日志的共享。当备用Namenode
接管工作之后,它将通过共享edits日志直至末尾,以实现与活动Namenode的状态同步,并继续读取由活动Namenode写入的新条目。
      (2)Datanode需要同时向两个Namendoe发送数据块处理报告,因为数据块的映射信息存储在Namenode的内存中,而非磁盘。
      (3)客户端需要使用特定的机制来处理Namenode的失效问题,这一机制对用户透明。
      (4)辅助Namenode的角色被备用Namendoe所包含,内用Namenode为活动的Namenode命名空间设置周期性检查点。
      HDFS的共享存储是通过群体日志管理器(Quorum Journal Manager,QJM)实现的。QJM以一组日志节点(journal node)的形式运行,每一次edits必须写入多数日志节点。典型的,有3个journal节点,所以系统能够忍受其中任何一个的丢失QJM的实现虽然类似ZooKeeper,但是并没有使用到它。然而,HDFS HA在选取活动的Namendoe时确实使用了ZooKeeper技术。

3.11.2 NFS与QJM

(1)NFS(Network File System 网络文件系统)
      NFS作为active Namenode和standby Namenode之间数据共享的存储。active Namenode会把最近的edits文件写到NFS,而standby Namenode从NFS中把数据读过来。这个方式的缺点是:如果active或standby有一个和NFS之间网络有问题,则会造成他们之前数据的同步出问题。并且不能保证同一时间只有一个Namenode向NFS中写入数据。
(2)QJM(Quorum Journal Manager 群体日志管理器,目前hadoop2.x使用)
      QJM是一个专用的HDFS实现,提供了一个高可用的编辑日志。这种方式可以解决上述NFS容错机制不足的问题。同一时间QJM仅允许一个Namenode向编辑日志中写入数据。active和standby之间是通过一组日志节点journal node(数量是奇数,可以是3,5,7…,2n+1)来共享数据。active把最近的edits文件写到2n+1个journal node上,只要有n+1个写入成功,就认为这次写入操作成功了。然后standby就可以从journalnode上读取了。QJM方式有容错的机制,可以容忍n个journalnode的失败。

3.11.3 failover故障切换

      系统中有一个称谓故障转移控制器(failover controller),管理着将活动Namenode转移为备用Namenode的转换过程。有多种故障转移控制器,但默认的一种是使用了ZooKeeper来确保有且仅有一个活动Namenode。每一个Namenode运行着一个轻量级的故障转移控制器。其工作就是监视宿主Namenode是否失效(通过一个简单的心跳机制实现)并在Namenode失效时进行故障转移。管理员也可以手动发起故障转移,例如在日常维护时。
(1)Failover失效转移
      两个节点之间通过一个串口或网线作为心跳线互相连接,平时只有Active节点工作。心跳断了后,主节点不管正不正常,都自动关闭,备份节点自动接管,并且升级为主节点,从而保持系统正常运行。当失效节点修好重新集成进来后,仍然保持为从节点。这样的切换过程被称为Failover。
(2)自动failover
      Automatic Failover中,增加了2个新的组件:ZooKeeper集群,ZKFailoverController进程(简称为ZKFC)。HDFS的自动故障转移主要由ZooKeeper和ZKFC两个组件组成。
      1)ZooKeeper
      ZooKeeper是一个高可用的调度服务,可以保存一系列调度数据。自动failover的实现将依赖于Zookeeper的几个特性:
      ① 故障监控。每个NameNode将会和ZooKeeper建立一个持久session,如果NameNode失效,那么此session将会过期失效。此后ZooKeeper将会通知另一个Namenode,然后触发【Failover】失效转移。
      ② NameNode选举。ZooKeeper提供了简单的机制来实现active node选举。如果当前active失效,standby将会获取一个特定的排他锁(lock),那么获取锁的Node接下来将会成为active。原active状态的NameNode将会强制下线。
      2)ZKFC
      ZKFailoverControllor(ZKFC)是一个zookeeper客户端。它主要用来监测和管理Namenodes的状态,每个Namenode机器上都会运行一个ZKFC程序。它的职责为:
      ① 健康监控:ZKFC间歇性地使用health-check指令ping Namenode,Namenode也会及时反馈健康状况。如果Namenode失效,或者unhealthy,或者无响应,那么ZKFS将会标记其为“unhealthy”。
      ② ZooKeeper会话管理:当本地Nanenode运行良好时,ZKFC将会持有一个ZooKeeperSession。如果本地Namenode为Active,它同时也持有一个“排他锁”(znode)。该lock在ZooKeeper中为临时节点。如果session过期,那么该lock所对应的znode也将被删除。
      ③ ZooKeeper选举:如果本地Namenode运行良好,并且ZKFS没有发现其他的Namenode持有lock(如Active失效后释放lock)。它将尝试获取锁,如果获取成功即“赢得了选举”,此后将会把该Namenode标记为Active。然后触发Failover:首先,调用fencing method(规避方法),然后,把本地Namenode提升为Active。

3.11.4 failover脑裂现象(brain-split)

      脑裂是指在主备切换时,由于切换不彻底或其他原因,导致客户端和DataNode看到两个active NameNode,最终使得整个集群处于混乱状态。那么两个NameNode将会分别有两种不同的数据状态,可能会导致数据丢失或状态异常。故障规避:高可用实现做了更进一步的优化,以确保先前活动的Namenode 不会执行危害系统并导致系统崩溃的操作,该方法称为"规避" (fencing) 。
(1)解决脑裂问题,通常采用规避(Fencing)机制,包括三个方面:
      ① 共享存储fencing:确保只有一个Namenode往共享存储中写数据。
      ② 客户端fencing:确保只有一个Namenode可以响应客户端的请求。
      ③ DataNode fencing:确保只有一个Namenode可以向DataNode下发命令。
(2)规避手段
      同一时间QJM仅允许一个Namenode向edits日志中写入数据。然而,对于先前的活动Namenode而言,仍有可能响应并处理客户过时的读请求,因此,设置一个SSH规避命令用于杀死Namenode的进程。
      规避手段:① 撤销Namenode访问共享存储目录的权限;②通过远程命令屏蔽响应的网络端口;③通过STONITH(一枪爆头:shoot the other node in the head)的技术进行隔离,该方法主要通过一个特定的供电单元对相应的主机进行断电操作。

3.12 异构层级存储结构

(1)异构存储概述
      异构存储可以根据各个存储介质读写特性的不同发挥各自的优势。针对冷数据,采用容量大的、读写性能不高的介质存储,比如最普通的磁盘;对于热数据,可以采用SSD(固态硬盘,读写速度快,容量小)的方式进行存储。
(2)异构存储的机制
      ① DataNode通过心跳汇报自身数据存储目录的StorageType给NameNode;② NameNode进行汇总并更新集群内各个节点的存储资源情况;③ 待复制文件根据自身设定的存储策略信息向NameNode请求拥有此类型存储介质的DataNode作为候选节点。
(3)异构存储类型
      根据存储速度,从快到慢:RAM_DISK(true)、SSD (false)、DISK (false)、ARCHIVE (false)(一种高密度的存储方式,一般用于冷数据的存储)。true或者false代表此类存储类型是否为transient,即转瞬即逝的。在HDFS中,如果没有主动声明数据目录存储类型,默认都是DISK类型。每个节点是由多种异构存储介质构成的,hdfs-site.xml的配置文件如下:

<property>
<name>dfs.datanode.data.dir</name>
<value>[disk]/dir0,[disk]/dir1,[ssd]/dir2,[ssd]/dir3</value>
</property>

(4)存储策略
在这里插入图片描述
      在上表中,前两列分别是策略编号和策略名;第三列表示在有n个副本的情况下,各个副本都会被存放在什么类型的存储上;第四列表示在创建文件(写入第一个副本)时第三列指定的存储无法满足要求时备选的存储类型;第五列表示在生成副本时第三列指定的存储无法满足要求时备选的存储类型。以One_SSD策略为例,正常情况一个副本放在有SSD标签的存储上,其他副本放在DISK标签的存储上,在正常情况无法得到满足的情况下,副本可能会被“强制”放在SSD或者DISK上。
      按照All_SSD->One_SSD->Hot->Warm->Cold的顺序,面向的数据“越来越冷”,可以根据数据的冷热程度选择合理的策略。Lazy_Persist比较特殊,如果一个文件的存储策略被指定为Lazy_Persist,在写入时会先写入内存,再异步地写入磁盘,官方文档中提到“Applications can choose to use Lazy Persist Writes to trade off some durability guarantees in favor of reduced latency.”,即主要用来降低小数据量的写入延迟,代价是在某些情况下会有数据丢失。
      与存储策略相关的配置项为:dfs.storage.policy.enabled,默认为true,即默认支持存储策略。
      与存储策略相关的命令和操作:
      ① 列出当前版本支持的存储策略:
      hdfs storagepolicies -listPolicies
      ② 为执行路径设置指定策略:
      hdfs storagepolicies -setStoragePolicy -path -policy
      ③ 取消策略:
      hdfs storagepolicies -unsetStoragePolicy -path
      ④ 获取指定路径的策略:
      hdfs storagepolicies -getStoragePolicy -path
(5)HDFS Mover
      在修改了文件的存储策略后,目前的HDFS(Hadoop 2.8.0)不支持自动调整文件的数据块副本到正确类型的存储上,需要使用一个专门的工具Mover。用法如下:
       hdfs mover [-p <files/dirs> | -f <local file name>]
       -p 指定要迁移的文件/目录(以空格分隔)
       -f 指定一个本地文件路径,该文件包含需要迁移的文件或者目录列表

3.13 HDFS ACL(访问控制列表)

      hdfs-site.xml文件配置如下:

<property>
    <name>dfs.permissions.enabled</name>
    <value>true</value>
</property>
<property>
    <name>dfs.namenode.acls.enabled</name>
    <value>true</value>
</property>

(1)HDFS的权限管理分成2大部分:
      1)类似linux的基本权限管理(粗粒度),针对管理对象分三种:user、group、other方式的权限管理方式:
      ①user:即目录或文件的owner;
      ②group:即上述owner所在的组;
      ③other:其他用户的统称。
      2)ACL方式的权限管理(细粒度),可以精确控制到某个user、某个group具有对应的权限。
(2)应用客户端配置的uMask
      这个配置是客户端可以配置的,即客户端自己主宰创建文件或目录的权限。
      uMask的作用:就是对HDFS基本权限的限制,如022分别表示:0表示对owner没有限制,2表示对group不允许有写权限,2表示对other不允许有写权限。
      应用客户端配置的uMask其实就是拿用户设置的权限减去上述uMask权限,就得到文件或目录的最终权限了。如创建目录,默认777权限,然后减去umask权限022就等于755,即默认情况下owner拥有读写和可执行权限,owner所在group拥有读和可执行权限,other拥有读和可执行的权限。
      uMask的获取过程:
      ①首先尝试获取配置文件中的fs.permissions.umask-mode属性,如022,然后构建出uMask权限对象;
      ②如果上述属性没有配置的话,获取dfs.umask属性(过时了,推荐用第一种方式),这种方式的uMask是10进制形式,如上述的022=2x8+2=18即这里的uMask要配置成18;
      ③ 如果上述属性也没有配置的话,默认使用18即022来构建出uMask对象。

3.14 HDFS的中心化缓存
3.14.1 概述

      HDFS上的中心化缓存是一个显式的缓存机制, 使得用户可以指定哪个路径被缓存。NameNode和拥有指定文件块的DataNode们通讯, 并命令他们将文件块缓存到堆外缓存中。
HDFS的中心化缓存管理有如下的明显优势:
      (1)明确的固定(pinning)可以防止被频繁使用的数据被从内存中移除。这对那些数据尺寸超过主存的情况很重要, 而这类情况在HDFS上是非常常见。
      (2)因为DataNode的缓存是由NameNode管理的, 应用方可以查询被缓存的文件块的位置, 并决定任务的位置。将任务和缓存的文件块副本放在一起改善了读的性能。
      (3)当文件块被DataNode缓存后, 客户端可以使用更新, 更高效, 和零拷贝的读API。因为缓存文件块的校验和验证由DataNode执行了一次, 客户端就能以0开销地使用新API(问: 什么新API?).
      (4)中心化缓存可以改善整个集群的内存使用情况。当只能依靠各个DataNode的操作系统高速缓存时, 重复地读取一块文件块会导致该块的n个拷贝被读入高速缓存。使用中心化缓存管理后, 一个用户可以显示指定只有n个副本中的m个可以被固定在缓存中, 并节省n-m的内存。

3.14.2 HDFS缓存原理

在这里插入图片描述
      在这个架构中, NameNode负责协调集群中所有DataNode的堆外缓存. NameNode定期地接收各个DataNode发送过来的缓存报告, 报告描述了任意DataNode上被缓存的所有文件块。NameNode通过在心跳响应中发送的缓存和踢出缓存命令来管理DataNode上的缓存。
      NameNode查询自身的缓存指令(directives?)来决定哪些路径应该被缓存. 缓存指令被永久存储在文件系统映像(fsimage)和操作日志(edit log)中, 并且可以把java和命令行的api进行添加,删除和修改. NameNode也存储了一些缓存池, 这些管理实体被用来将缓存指令分组, 进行资源管理和权限控制。
      NameNode定期地扫描命名空间和缓存指令来决定,哪些文件块要被缓存,哪些要踢出缓存,并将缓存动作指派到DataNode上。扫描亦可由用户动作发起,例如添加或者删除缓存指令,或删除一个缓存池。
不会缓存那些,构建中,损坏或者不完整的文件块。如果一个缓存指令涉及到文件链接,这个链接的目标文件不会被缓存。

3.14.3 实现情况

      用户需通过命令显式的将一个目录或文件加入/移除缓存:①不支持块级别的缓存;②不支持自动化缓存;③可设置缓存失效时间。
      缓存目录:仅对一级文件进行缓存,不会递归缓存所有文件与目录。
      以pool的形式组织缓存资源:①借助YARN的资源管理方式,将缓存划分到不同pool中;②每个pool有类linux权限管理机制、缓存上限、失效时间等。
      独立管理内存,未与资源管理系统YARN集成。用户可为每个DN设置缓存大小,该值独立于YARN。

参考文章:
[1] 《Hadoop权威指南》
[2] https://blog.youkuaiyun.com/lb812913059/article/details/79718303
[3] https://www.cnblogs.com/BYRans/p/5128162.html
[4] https://blog.youkuaiyun.com/lb812913059/article/details/79718483
[5] https://blog.youkuaiyun.com/u014561871/article/details/80332532
[6] https://blog.youkuaiyun.com/regan_hoo/article/details/78584439
[7] https://my.oschina.net/pingpangkuangmo/blog/699527
[8] https://www.jianshu.com/p/b60d5d24174c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值