HDFS详解
1.HDFS的设计
1.1.HDFS使用场景
-
超大文件----在这里指具有几百MB几百GB甚至几百TB大小的文件。目前已经存储PB级数据的hadoop集群了;
-
流式数据访问----一次写入、多次读取是高效的访问模式。数据集通常由数据源生成或从数据源复制而来,接着长时间在此数据集上进行各类分析。每次分析都会涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要;
-
商用硬件----hadoop并不需要运行在昂贵且高可用的硬件上。他是设计运行在商用硬件的集群上的,因此至少对于庞大的集群来说,节点故障的几率还是非常高的。HDFS遇到上述故障时,被设计成能够继续运行且不让用户察觉到的中断。
1.2.HDFS不适用的场景
-
低时间延迟的数据访问----要求低时间延迟的访问的应用,不适合在HDFS上运行。记住,HDFS是为高数据吞吐量应用优化的,这可能会以高时间延迟为代价。目前,对于低延迟的访问需求,hbase是更好的选择。
-
大量的小文件----由于nameNode将文件系统的元数据存储在内存中,因此该文件系统所能存储的文件总量受限于nameNode的内存总容量。根据经验,每个文件、目录和数据块的存储信息大约占150字节。过多的小文件存储会大量消耗nameNode的存储量。
-
多用户写入,任意修改文件----HDFS中的文件只有一个Writer,而且写操作总是将数据添加在文件的末尾。他不支持具有多个写入者的操作,也不支持在文件的任意位置进行修改。
2.HDFS的概念
2.1.数据块
每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位,构建于单个磁盘之上的文件系统通过磁盘块来管理该文件系统中的块,该文件系统块的大小可以是磁盘块的整数倍。HDFS同样也有块的概念,但是大得多,默认为64MB。与单一磁盘上的文件系统相似,HDFS上的文件也被划分为多个分块,作为独立的存储单元。与其他文件系统不同的是,HDFS中小于一个块大小的文件不会占据整个块的空间。
对分布式文件系统中的块进行抽象会带来很多好处:
-
第一个明显的好处是,一个文件的大小可以大于网络中任意一个磁盘的容量。文件的所有块并不需要存储在同一个磁盘上,因此他们可以利用集群上的任意一个磁盘进行存储。
-
第二个好处是,使用块抽象而非整个文件做为存储单元,大大简化了存储子系统的设计。简化是所有系统的目标,但是这对于故障种类繁多的分布式系统来说尤为重要。将存储子系统控制单元设置为块,可简化存储管理(由于块的大小是固定的,因此计算单个磁盘能够存储多少个块相对容易)。同时也消除了对元数据的顾虑(块只是存储数据的一部分---而文件的元数据,如权限信息,并不需要与块一同存储,这样一来,其他的系统就可以单独管理这些元数据)。
-
块非常适合用于数据备份进而提供数据容错能力和可用性。将每个块复制到少数几个独立的机器上(默认为3个),可以确保在发生块、磁盘或机器故障后数据不会丢失。如果发现一个块不可用,系统会从其他地方读取另一个复本,而这个过程对用户是透明的。
3.HDFS构成
HDFS集群由三部分构成,并以管理者-工作者模式运行。这三部分分别是:一个nameNode(管理者)、一个secondNameNode(namenode辅助系统)和多个datanode(工作者)。并且用户通过客户端(client)与hdfs进行通讯交互。
3.1.namenode
nameNode管理文件系统的命名空间。他维护着文件系统树及整棵树内所有的文件和目录。这些信息以两个文件形式永久保存在本地磁盘上:命名空间镜像文件(fsimage)和编辑日志文件(editlog)。nameNode也记录着每个文件中各个块所在的数据节点信息,但它并不会永久保存块的位置信息,因为这些信息会在系统重启时由数据节点重建。
3.2.SecondnameNode
文件系统客户端在执行写操作时,这些操作首先被记录到编辑日志中,然后更新nameNode内存中维护的文件系统的元数据;命名空间镜像文件(fsimage)是文件系统元数据的一个永久检查点,因为fsimage文件是一个大型文件,如果频繁的执行写操作,会使系统运行极为缓慢。但是如果不进行操作,editlog文件会无限增长,一旦nameNode需要恢复,则需要花费非常长的时间,所以hdfs引入了辅助nameNode,为主nameNode内存中的文件系统元数据创建检查点。最终,实现将主nameNode的编辑日志和命名空间镜像文件合并。形成一个更小的editlog文件和最新的fsimage文件。
3.3.Datanode
Datanode是文件系统的工作节点。他们根据需要存储并检索数据块(受客户端或namenode调度),并且定期向nameNode发送他们所存储的块的列表。
3.4.Client(客户端)
客户端代表用户通过与nameNode和datanode交互来访问整个文件系统。客户端提供了一个类似于POSIX(可移植操作系统界面)的文件系统接口,因此用户在编程时无需知道nameNode和datanode也可以实现其功能。
3.5.nameNode容错
如果没有nameNode,文件系统将无法使用。事实上,如果运行nameNode服务的机器损毁,文件系统上所有的文件都将会丢失,因为我们不知道如何根据datanode的块来重建文件。因此,对nameNode实现容错非常重要,hadoop为此提供了两种机制:
-
第一种机制是备份那些组成文件系统元数据持久状态的文件。Hadoop可以通过配置使nameNode在多个文件系统中保存元数据的持久状态。这些写操作是实时同步的,是原子操作。一般的配置是,将持久状态写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS)
-
另一种可行的方式是运行一个辅助nameNode,但它不能被作为nameNode。这个辅助nameNode的重要作用是定期通过编辑日志合并命名空间镜像,以防止编辑日志过大。这个辅助nameNode一般在另外一台单独的物理计算机上运行,因为它需要占用大量cpu时间和与nameNode相同容量的内存来执行合并操作。他会保存合并后的命名空间镜像的副本,并在nameNode发生故障时启用。但是,辅助nameNode保存的状态总是滞后于主节点,所以在主节点全部失效时,难免会丢失部分数据。在这种情况下,一般把存储在NFS上的nameNode元数据复制到辅助nameNode并作为新的主nameNode运行。
3.6.HDFS中的文件访问权限
针对文件和目录,HDFS有与POSIX非常相似的权限模式。
一共提供三类权限模式:只读权限(r)、写入权限(w)和可执行权限(x)。读取文件或列出目录内容时需要只读权限。写入一个文件,或是在一个目录上创建及删除文件或目录,需要写入权限。对于文件而言,可执行权限可以忽略,因为你不能在HDFS中执行文件(与POSIX不同),但在访问一个目录的子项时需要该权限。
每个文件和目录都有所属用户(owner)、所属组别(group)及模式(mode)。这个模式是由所属用户的权限、组内成员的权限及其他用户的权限组成的。
默认情况下,可以通过正在运行进程的用户名和组名来唯一确定客户端的标示。但由于客户端是远程的,任何用户都可以简单的在远程系统上以他的名义创建一个账户来进行访问。因此,作为共享文件系统资源和防止数据意外损失的一种机制,权限只能供合作团体中的用户使用,而不能在一个不友好的环境中保护资源。注意,最新的hadoop系统支持kerberos用户认证,该认证去除了这些限制。但是,除了上述限制之外,为防止用户或者自动工具及程序意外修改或删除文件系统的重要部分,启用权限控制还是很重要的。
注意:这里有一个超级用户的概念,超级用户是nameNode进程的标识。对于超级用户,系统不会执行任何权限检查。
4.数据流
4.1.文件读取剖析
-
客户端通过调用FileSystem对象的open()方法来打开希望读取的文件,对于HDFS来说,这个对象是分布式文件系统的一个实例。
-
DistributedFileSystem通过使用RPC来调用nameNode,以确定文件起始块的位置。对于每一个块,nameNode返回存有该块复本的datanode地址。此外,这些datanode根据他们与客户端的距离来排序。如果客户端本身就是一个datanode,并保存有相应数据块的一个复本时,该节点将从本地datanode中读取数据。
-
DistributedFileSystem类返回一个FSDataInputStream对象给客户端读取数据。FSDataInputStream类转而封装DFSInputStream对象,该对象管理着datanode和nameNode的I/O。
-
接着,客户端对这个输入流调用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很快会成为一个瓶颈。
4.2.文件写入剖析
-
首先客户端通过DistributedFileSystem上的create()方法指明一个预创建的文件的文件名(第一步),DistributedFileSystem再通过RPC调用向NameNode申请创建一个新文件(第二步,这时该文件还没有分配相应的block)。namenode检查是否有同名文件存在以及用户是否有相应的创建权限,如果检查通过,namenode会为该文件创建一个新的记录,否则的话文件创建失败,客户端得到一个IOException异常。DistributedFileSystem返回一个FSDataOutputStream以供客户端写入数据,与FSDataInputStream类似,FSDataOutputStream封装了一个DFSOutputStream用于处理namenode与datanode之间的通信。
-
当客户端开始写数据时(第三步),DFSOutputStream把写入的数据分成包(packet), 放入一个中间队列——数据队列(data queue)中去。DataStreamer从数据队列中取数据,同时向namenode申请一个新的block来存放它已经取得的数据。namenode选择一系列合适的datanode(个数由文件的replica数决定)构成一个管道线(pipeline),这里我们假设replica为3,所以管道线中就有三个datanode。DataSteamer把数据流式的写入到管道线中的第一个datanode中(第四步),第一个datanode再把接收到的数据转到第二个datanode中(第四步),以此类推。
-
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数。
-
当客户端完成写数据后,它会调用close()方法(第六步)。这个操作会冲洗(flush)所有剩下的package到pipeline中,等待这些package确认成功,然后通知namenode写入文件成功(第七步)。这时候namenode就知道该文件由哪些block组成(因为DataStreamer向namenode请求分配新block,namenode当然会知道它分配过哪些blcok给给定文件),它会等待最少的replica数被创建,然后成功返回。
注意:①hdfs在写入的过程中,有一点与hdfs读取的时候非常相似,就是:DataStreamer在写入数据的时候,每写完一个datanode的数据块(默认64M),都会重新向nameNode申请合适的datanode列表。这是为了保证系统中datanode数据存储的均衡性。
②hdfs写入过程中,datanode管线的确认应答包并不是每写完一个datanode,就返回一个确认应答,而是一直写入,直到最后一个datanode写入完毕后,统一返回应答包。如果中间的一个datanode出现故障,那么返回的应答就是前面完好的datanode确认应答,和故障datanode的故障异常。这样我们也就可以理解,在写入数据的过程中,为什么数据包的校验是在最后一个datanode完成。
4.3.网络拓扑与hadoop
衡量节点间的带宽,实际上很难实现(他需要一个稳定的集群,并且在集群中两两节点对数量是节点数量的平方),为此hadoop采用了一个简单的方法,把网络看做一棵树,两个节点间的距离是他们到最近的共同祖先的距离总和。该树中的层次是没有预先设定的,但是相对于数据中心,框架和正在运行的节点,通常可以设定等级。具体的想法是对于以下每个场景,可用带宽依次递减:
- 同一节点中的进程
- 同一机架上的不同节点
- 同一数据中心不同机架上的节点
- 不同数据中心的节点
- Distance(/d1/r1/n1,/d1/r1/n1)=0(同一节点中的进程)
- Distance(/d1/r1/n1,/d1/r1/n2)=2(同一机架上的不同节点)
- Distance(/d1/r1/n1,/d1/r2/n3)=4(同一数据中心不同机架上的节点)
- Distance(/d1/r1/n1,/d2/r3/n4)=6(不同数据中心中的节点)
如下图所示:
最后,我们必须意识到hadoop无法自行定义网络拓扑结构。他需要我们能够理解并辅助定义。不过在默认情况下,假设网络是平铺的—仅有单一层次—换句话说,所有节点都在统一数据中心的同一机架上。小的集群可能如此,所以不需要进一步配置。
4.4.复本的布局
Hadoop的默认布局策略是在运行的节点上放第一个复本(如果客户端运行在集群之外,就随机选择一个节点,不过系统会避免挑选那些存储太满或太忙的节点)。第二个复本放在与第一个不同且随机另外选择的机架上节点上。第三个复本与第二个复本放在相同的机架,且随机选择另外一个节点。其他复本放在集群中随机选择的节点上,不过系统会尽量避免在相同的机架上放太多复本。
一旦选定复本的放置位置,就会根据网络拓扑创建一个管线。如果副本数为3,则有如下管线:
总的来说,这一方法不仅提供很好的稳定性,并实现很好的负载均衡,包括写入带宽(写入操作只需要遍历一个交换机)、读取性能(可以从两个机架中进行选择读取)和集群中块的均匀分布(客户端只能在本地机架写入一个块)。
4.5.一致性模型
文件系统的一致模型描述了对文件读/写的数据可见性。HDFS为性能牺牲了一些POSIX要求,因此一些操作与你期望的可能不同。 创建一个文件之后,文件名在文件系统的命名空间中立即可见,但是写入文件的内容并不能保证立即可见,即使数据流已经刷新并存储。
在HDFS中当写入的数据超过一个块后,新的reader能够看见第一个块。之后的块也不例外。总之,其他reader无法看见当前正在写入的块。
HDFS提供了一个方法来强制所有的缓存与数据节点同步,即对FSDataOutputStream调用sync()方法。当sync()方法返回成功后,对所有新的reader而言,HDFS能保证文件中到目前为止写入的数据均可见且一致。
4.6.HDFS数据的完整性
HDFS会对写入的所有数据计算校验和,并在读取数据时验证校验和。他针对每个由io.bytes.per.checksum指定字节的数据计算校验和。默认情况下为512个字节,由于CRC-32校验和是4个字节,所以存储校验和的额外开销低于1%。
客户端在写数据时,将数据及其校验和发送到一系列datanode组成的管线,管线中最后一个datanode负责验证校验和。如果datanode检测到错误,客户端便会收到一个ChecksumException异常。
客户端从datanode读取数据时,也会验证校验和,将他们与datanode中存储的校验和进行比较。每个datanode均持久保存一个用于校验的校验和日志,所以她知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会告诉这个datanode,datanode由此更新日志。保存这些统计信息对于检测损坏的磁盘很有价值。
不只是客户端在读取数据时会验证校验和,每个datanode也会在一个后台线程中运行一个dataBlockScnner,从而定期验证存储这个datanode上的所有数据块。该项措施是解决物理存储媒体上损坏的有力措施。
由于HDFS存储着每个数据块的复本,因此他可以通过复制完好的数据复本来修复损坏的数据块,进而得到一个新的、完好无损的复本。基本思路是,客户端在读取数据时,如果检测到错误,就向namenode报告已损坏的数据块及其正在尝试读操作的这个datanode,最后才抛出ChecksumException异常。 Namenode将这个已损坏的数据块的复本标记为已损坏,所以并不需要直接与datanode联系,或尝试将这个复本复制到另一个datanode。之后,它安排这个数据块的一个复本复制到另一个datanode,如此一来,数据块的复本因子又回到期望水平。此后,已损坏的数据块复本便被删除。
5.HDFS永久性数据存储
5.1.Namenode的目录结构
Namenode被格式化之后,将产生如下所示的目录结构:
${dfs.name.dir}/current/VERSION
/edits
/fsimage
/fstime
Dfs.name.dir属性描述了一组目录,各个目录存储的内容相同。这个机制使系统具备了一定的复原能力,特别是当其中一个目录位于NFS之上时。
1、VERSION文件是一个java属性文件,其中包含正在运行的HDFS的版本信息。
①layoutVersion是一个负整数,描述HDFS永久性数据结构(也称布局)的版本,但是这个版本号与hadoop发布包的版本号无关;只要布局变更,版本号便会递减,HDFS也需要升级。否则,磁盘仍然使用旧版本的布局,新版的namenode就无法正常工作;
②namespaceID是文件系统的唯一标识符,是在文件系统首次格式化时设置的。任何datanode在注册到namenode之前都不知道namespaceID值,因此,namenode可以使用该属性鉴别新建的datanode。
③cTime标记了namenode存储系统的创建时间。对于刚刚格式化的存储系统,这个属性值为0;但在文件系统升级后,该值会更新到最新的时间戳。
④storageType说明该存储目录包含的是namenode的数据结构。
2、edits:用来保存namenode的编辑日志,当客户端执行写操作时,这些操作首先被记录到编辑日志中。然后在更新namenode内存中维护的文件系统元数据。
3、fsimage文件是文件系统元数据的一个永久性检查点(包含文件系统中的所有目录和文件inode的序列化信息)。并非每个写操作都会更新这个文件,因为fsimage是一个大型文件,如果频繁执行写操作,会使系统运行极为缓慢。当namnode发生故障时,可以把fsimage文件载入到内存中重构新近的元数据,在执行编辑日志中的各项操作。
4、fstime:用来记录fsimage更新时间
5.2.辅助namenode的目录结构
创建检查点的过程不仅为主namenode创建检查点数据,还使辅助namenode最终也有一份检查点数据(存储在previous.checkpoint子目录中)。这份数据可用作namenode元数据的备份。
${fs.checkpoint.dir}/current/VERSION
/edits
/fsimage
/fstime
/previous.checkpoint/VERSION
/edits
/fsimage
/fstime
辅助namenode的previous.checkpoint目录、辅助namenode的current目录和主namenode的current目录的布局相同。这种设计方案的好处是,在主namenode发生故障时,可以从辅助namenode恢复数据。一种方法是将相关存储目录复制到新的namenode中;另一种方法是使用-importcheckpoint选项重启辅助namenode守护进程,从而将辅助namenode用作新的主namenode。借助这个选项,dfs.name.dir属性定义的目录中没有元数据时,辅助namenode就从fs.checkporint.dir目录载入最新的检查点数据,因此,无需担心该操作会覆盖现有的元数据。
5.3.Datanode的目录结构
和namenode不同的是,datanode的存储目录是启动时自动创建的,不需要额外的格式化。Datanode的关键文件和目录如下所示:
${dfs.data.dir}/current/VERSION
/blk_<id_1>
/blk_<id_1>.meta
/blk_<id_2>
/blk_<id_2>.meta
… …
/blk_<id_64>
/blk_<id_64>.meta
/subdir0/
/subdir1/
… …
/subdir63/
1、Datanode的VERSION文件与namenode的VERSION文件非常相似,如下所示:
其中:namespaceID属性、cTime属性和layoutVersion属性的值与namenode中的值相同。实际上,namespaceID是datanode首次访问namenode的时候从namenode处获取的。各个datanode的storageID都不相同,namenode可用这个属性来识别datanode,storageType表明这个目录是datanode的存储目录。
2、datanode的current目录中的其他文件都有blk_前缀,包括两种文件类型:HDFS块文件(仅包含原始数据)和块的元数据文件(包.meta后缀)。块文件包含所有的存储文件的原始数据;元数据文件包括头部(含版本和类型信息)和该块各区段的一系列的校验和。
当目录中数据块的数量增加到一定规模时,datanode会创建一个子目录来存放新数据块及其元数据信息。当前目录已经存储64个(通过dfs.datanode.numblocks属性设置)数据块时,会创建一个子目录。终极目标是设计一棵高扇出的树,即使文件系统中的块非常多,也只需要访问少数几个目录级别就可获取数据。通过这种方式,datanode可以有效管理各个目录中的文件,避免大多数操作系统遇到的难题-----即很多文件存放在一个目录之中。
如果dfs.data.dir属性指定了不同磁盘上的多个目录,那么数据块会以“轮转”的方式写到各个目录中。注意,同一个datanode上的每个磁盘上的块不会重复,不同datanode之间的块才可能重复。
5.4.辅助namenode工作原理
客户端执行写操作的时候,首先更新编辑日志(edits),然后修改内存中的文件系统映射。随着时间的推移,编辑日志会非常大。这个时候,就需要将编辑日志和文件系统映射持久检查点(fsimage)进行合并,以减少编辑日志的大小。这个工作就是由辅助namenode完成的。
辅助namenode工作原理如下:
①辅助namenode请求主namenode停止使用edits文件,暂时将新的写操作记录到一个新的文件中;
②辅助namenode从主namenode获取fsimage和edits文件(通过http get)
③辅助namenode将fsimage文件载入内存,逐一执行edits文件中的操作,创建新的fsimage文件。
④辅助namenode将新的fsimage文件发送回主namenode(使用http post).
⑤主namenode用从辅助namenode接收的fsimage文件替换旧的fsimage文件;用步骤1所产生的edits文件替换旧的edits文件。同时,还更新fstime文件来记录检查点执行时间。
最终,主namenode拥有最新的fsimage文件和一个更小的edits文件。当namenode处在安全模式时,管理员也可调用hadoop dfsadmin –saveNameSpace命令来创建检查点。
从上面的过程中我们清晰的看到辅助namenode和主namenode拥有相近内存需求的原因(因为辅助namenode也把fsimage文件载入内存)。因此,在大型集群中,辅助namenode需要运行在一台专用机器上。
创建检查点的触发条件受两个配置参数控制。通常情况下,辅助namenode每隔一小时(有fs.checkpoint.period属性设置)创建检查点;此外,当编辑日志的大小达到64MB(有fs.checkpoint.size属性设置)时,也会创建检查点。系统每隔五分钟检查一次编辑日志的大小。
5.5.安全模式
Namenode启动时,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各项操作。一旦在内存中成功建立文件系统元数据的映像,则创建一个新的fsimage文件(这个操作不需要辅助namenode)和一个空的编辑日志。此时,namenode开始监听RPC和http请求。但此时,namenode运行在安全模式,即namenode的文件系统对于客户端来说是只读的。
需要强调的是,系统中数据块的位置并不是由namenode维护的,而是以块列表的形式存储在datanode中。在系统的正常操作期间,namenode会在内存中保留所有块位置的映射信息。在安全模式下,各个datanode会向namenode检查块列表信息(即向namenode发送块列表的最新情况),namenode了解到足够多的块位置信息之后,即可高效运行文件系统。但如果namenode没有检查到足够多的块复本,则需要将块复制到其他datanode,而在大多数情况下这都是不必要的(因为只需等待检查到最小复本数),并会极大的浪费集群的资源。实际上,在安全模式下,namenode并不向datanode发出任何块复制或块删除的指令。
如果满足“最小复本条件”namenode会在30秒之后退出安全模式。所谓的最小复本条件指的是文件系统中有99.9%的块满足最小复本级别(默认值是1,由dfs.replication.min属性设置)。
①查看namenode是否处于安全模式:hadoop dfsadmin –safemode get
②执行某条命令前namenode先退出安全模式:hadoop dfsadmin –safe wait
③进入安全模式:hadoop dfsadmin –safemode enter
④离开安全模式:hadoop dfsadmin –safemode leave
6.HDFS日常维护
6.1.Datanode块扫描器
各个datanode运行一个块扫描器,定期检测节点上的所有块,从而在客户端读到坏块之前及时检测和修复坏块。可以依靠DataBlockScanner所维护的块列表依次扫描块,查看是否存在校验和错误。扫描器利用节流机制,来维持datanode的磁盘带宽。
默认情况下,扫描器每隔三周就会检测块,以应对可能的磁盘故障,这个周期由dfs.datanode.scan.period.hours属性设置。损坏的块报给namenode,并被及时修复。
6.2.均衡器
随着时间的推移,各个datanode上的块分布的越来越不均衡。不均衡的集群会降低MapReduce的本地性,导致部分datanode相对更为繁忙。应避免出现这种情况。
均衡器(balancer)程序是一个hadoop守护进程,它将块从繁忙的datanode移到相对空闲的datanode,从而重新分配块。同时坚持块复本放置策略,将复本分散到不同机架,以降低数据损坏率。他不断移动块,直到集群达到平衡,即每个datanode的使用率(该节点上已经使用的空间和空间容量之间的比率)和集群的使用率(集群中使用的空间与集群的空间容量之间的比率)非常接近,差距不超过给定的阀值。可调用下面指令启动均衡器运行:Start-balancer.sh。
-threshold参数指定阀值,以判断集群中是否均衡。这个标记是可选的;若不使用,默认阀值是10%。在任何时刻,集群中都只运行一个均衡器。均衡器会一直运行,直到集群变均衡。
为了降低集群负荷、避免干扰其他用户,均衡器被设计为在后台运行。在不同节点之间复制数据的带宽也是受限的。默认值是很小的1MB/s。可以通过hdfs-site.xml文件中的dfs.balance.bandwidthPerSec属性指定。
注意:在使用hdfs的过程中需要经常备份元数据和数据。元数据在namenode中;数据在datanode中,虽然datanode拥有多分数据备份,但是关键数据还是需要定期备份。如果要运行均衡器,需要在终端键入启动均衡器的指令,这就相当于我们的磁盘整理。这里说的均衡器是一个hadoop守护进程,我们启动均衡器以后,他就会在后台运行,运行达到我们设置的阀值后,均衡器自动停止。
6.3.向集群添加新节点
①将新节点的网络地址添加到include中(由dfs.hosts和mapred.hosts属性指定的文件;在cloudera manager管理中该文件叫dfs_hosts_allow.txt);
②运行一下指令,更新namenode的经过审批的一系列datanode集合:
Hadoop dfsadmin -refreshNodes
③以新节点更新slave文件。这样的话,hadoop控制脚本会将新节点包括在未来的操作中。
④启动新的datanode
⑤重启MapReduce集群
⑥检查新的datanode和TaskTracker是否出现在网页界面中
注意:hdfs不会自动将块从旧的datanode移到新的datanode以平衡集群,用户需要自己**均衡器运行。
6.4.从集群中删除节点
①将待解除节点的网络地址添加到exclude文件中(cloudera中叫dfs_hosts_exclude.txt),不更新include文件
②重启MapReduce集群,已终止在待解节点上运行的TaskTracker。
③执行一下指令,使用一系列新的审核通过datanode来更新namenode设置:
Hadoop dfsadmin -refreshNodes
④转到网页界面,查看待解除datanode的管理状态是否已经变为“Decommission in Progress”。将这些datanode的块复制到其他datanode中。
⑤当所有的datanode的状态变为“Decommissioned”时,表明所有块都已经复制完毕。关闭已经删除的节点。
⑥从include文件中删除这些节点,并执行一下命令:hadoop dfsadmin –refreshNodes。
⑦从slaves文件中删除节点
6.5.升级
①执行升级任务之前,确保前一升级已经定妥;
②关闭MapReduce,终止在taskTracker上运行任何孤儿任务。
③关闭hdfs,并备份namenode目录。
④在集群和客户端安装新版本的hadoop HDFS和MapReduce。
⑤使用-upgrade选项启动HDFS
⑥等待,直到升级完成
⑦检查HDFS是否运行正常
⑧启动MapReduce
⑨回滚或定妥升级任务(可选的)。