作者 | 大尊
hdfs是hadoop的分布式文件系统,即Hadoop Distributed Filesystem。下面主要讲下HDFS设计中的比较重要的点,使读者能通过简短的文章一窥HDFS的全貌,适合对HDFS有一点了解,但是对HDFS又感到困惑的初学者。本文主要参考的是hadoop 3.0的官方文档。
链接:http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html
当数据集的大小超过了一台物理机所能存储的能力时,就需要将它进行分区并存储到若干不同的独立的计算机上,其中管理跨多台计算机存储的文件系统称为分布式文件系统。
目录
使用HDFS的场景
HDFS的工作模式
文件系统命名空间(namespace)
数据复制
文件系统元数据的持久化
通讯协议
健壮性
数据组织
可访问性
存储空间回收
1、使用HDFS的场景
HDFS适合于以流式数据访问模式来存储超大的文件。即一次写入,多次读取,在数据集上长时间进行各种分析,每次分析都涉及该数据集数据的大部分甚至全部,对于超大文件,hadoop目前以支持存储PB级数据。
HDFS并不适合要求低时间延迟数据访问的应用,因为HDFS是为高数据吞吐量应用而优化的,这就有可能以时间延迟大为代价。
HDFS文件系统所能存储的文件总数受限于namenode的内存容量,根据经验,100百万的文件,且每个文件占一个数据块,那至少需要300MB的内存。
目前hadoop文件可能只有一个writer,而且写操作总是将数据添加在文件末尾,不支持在文件的任意位置进行修改。
相对于普通文件系统的数据块,HDFS也有块的概念,默认是128MB,HDFS上的文件也被划分成块大小的多个分块,作为独立的存储单元,不过HDFS中小于一个块大小的文件不会占据整个块的空间。如果没有特别指出,文中提到的块特指HDFS的块。
为何HDFS的块如此之大,其目的是为了最小化寻址开销。这个数也不能设置的过大,mapreduce中的map任务通常一次只处理一个块中的数据,因此如果任务数太少,作业的运行速度就会比较慢。
2、HDFS的工作模式
HDFS采用master/slave架构,即一个namenode(管理者)多个datanode(工作者)。
namenode负责管理文件系统的命名空间。维护着文件系统树和整个树内所有的文件和目录,这些信息都保存在两个文件中,命名空间镜像文件和编辑日志文件。namenode也记录了每个文件中各个块所在的数据节点信息。datanode是文件系统的工作节点,它们需要存储并检索数据块(受客户端或namenode调度),并定期向namenode发送它们所存储的块的列表。
如果没有namenode,文件系统将无法使用,因为我们不知道如何根据datanode的块重建文件,所以对namenode进行容错是非常重要的。为此hadoop提供了两种机制。
第一种机制是备份那些组成文件系统元数据持久状态的文件。一般,在将持久化文件写入本地磁盘的同时,写入远程挂载的NFS。
第二种方法是运行一个辅助namenode,这个辅助namenode定期通过编辑日志合并命名空间镜像,并在本地保存合并后的命名空间镜像的副本,在namenode发生故障时启用。但是在主节点失效时,难免会丢失部分数据,这时可以把存储在NFS的namenode元数据复制到辅助的namenode上作为新的namenode运行。这其中涉及到故障转移的机制。稍后会做一点分析。
3、文件系统命名空间(namespace)
HDFS支持传统的层次型文件组织结构。用户或者应用程序可以创建目录,然后将文件保存在这些目录里。
文件系统名字空间的层次结构和大多数现有的文件系统类似:用户可以创建、删除、移动或重命名文件。HDFS支持用户磁盘配额和访问权限控制,目前还不支持硬链接和软链接。但是HDFS架构并不妨碍实现这些特性。
Namenode负责维护文件系统的名字空间,任何对文件系统名字空间或属性的修改都将被Namenode记录下来。应用程序可以设置HDFS保存的文件的副本数目。文件副本的数目称为文件的副本系数,这个信息也是由Namenode保存的。
4、数据复制
HDFS被设计成能够在一个大集群中跨机器可靠地存储超大文件。它将每个文件存储成一系列的数据块,除了最后一个,所有的数据块都是同样大小的。
为了容错,文件的所有数据块都会有副本。每个文件的数据块大小和副本系数都是可配置的。 应用程序可以指定某个文件的副本数目。副本系数可以在文件创建的时候指定,也可以在之后改变。
HDFS中的文件都是一次性写入的,并且严格要求在任何时候只能有一个写入者。
Namenode全权管理数据块的复制,它周期性地从集群中的每个Datanode接收心跳信号和块状态报告(Blockreport)。当一个Datanode启动时,它会扫描本地文件系统,产生一个这些本地文件对应的所有HDFS数据块的列表,然后作为报告发送到Namenode,这个报告就是块状态报告。接收到心跳信号意味着该Datanode节点工作正常。块状态报告包含了一个该Datanode上所有数据块的列表。
数据块列表获取。查看数据块的健康状态:hdfs fsck / -files -block或者hdfs fsck /
HDFS的数据块存储在以_blk为前缀名的文件中,每个块还有一个相关的带有.meta后缀的元数据文件,元数据文件包括头部和该块各区段的一系列校验和。
当数据块的数量增加到一定规模时,datanode会创建一个子目录来存放新的数据块及元数据信息。如果当前目录已经存储了64个(通过dfs.datanode.numlocks属性设置)数据块时,就创建一个子目录,终极目标是设计一颗高扇出的目录树。
如果dfs.datanode.data.dir属性指定了不同磁盘的多个目录,那么数据块会以轮转(round-robin)的方式写入到各个目录中。
在每个datanode上也会运行一个块扫描器,定期检测本节点上的所有块,从而在客户端读取到坏块之前就及时的检测和修复坏块。默认情况下每隔3周会测试块的状态,并对可能的故障进行修复。
用户可以通过http://datanode:50070/blockScannerReport获取该datanode的块检测报告。
副本存放
副本的存放是HDFS可靠性和性能的关键。优化的副本存放策略是HDFS区分于其他大部分分布式文件系统的重要特性。这种特性需要做大量的调优,并需要经验的积累。HDFS采用一种称为机架感知(rack-aware)的策略来改进数据的可靠性、可用性和网络带宽的利用率。目前实现的副本存放策略只是在这个方向上的第一步。
通过一个机架感知的过程,Namenode可以确定每个Datanode所属的机架id。一个简单但没有优化的策略就是将副本存放在不同的机架上。这样可以有效防止当整个机架失效时数据的丢失,并且允许读数据的时候充分利用多个机架的带宽。这种策略设置可以将副本均匀分布在集群中,有利于当组件失效情况下的负载均衡。但是,因为这种策略的一个写操作需要传输数据块到多个机架,这增加了写的代价。
在大多数情况下,副本系数是3,HDFS的存放策略是将一个副本存放在本地机架的节点上,一个副本放在同一机架的另一个节点上,最后一个副本放在不同机架的节点上。这种策略减少了机架间的数据传输,这就提高了写操作的效率。
而在现实中,在hadoop2.0中,datanode数据副本存放磁盘选择策略有两种方式:
第一种是沿用hadoop1.0的磁盘目录轮询方式,实现类:
RoundRobinVolumeChoosingPolicy.java
第二种是选择可用空间足够多的磁盘方式存储,实现类:AvailableSpaceVolumeChoosingPolicy.java
第二种策略对应的配置项是:
如果不配置,默认使用第一种方式,既轮询选择磁盘来存储数据副本,但是轮询的方式虽然能够保证所有磁盘都能够被使用,但是经常会出现各个磁盘直接数据存储不均衡问题,有的磁盘存储得很满了,而有的磁盘可能还有很多存储空间没有得到利用,所有在hadoop2.0集群中,最好将磁盘选择策略配置成第二种,根据磁盘空间剩余量来选择磁盘存储数据副本,这样一样能保证所有磁盘都能得到利用,还能保证所有磁盘都被利用均衡。
在采用第二种方式时还有另外两个参数会用到:
默认值是10737418240,既10G,一般使用默认值就行。官方解释为,首先计算出两个值,一个是所有磁盘中最大可用空间,另外一个值是所有磁盘中最小可用空间,如果这两个值相差小于该配置项指定的阀值时,则就用轮询方式的磁盘选择策略选择磁盘存储数据副本。
默认值是0.75f,一般使用默认值就行。官方解释为,有多少比例的数据副本应该存储到剩余空间足够多的磁盘上。该配置项取值范围是0.0-1.0,一般取0.5-1.0,如果配置太小,会导致剩余空间足够的磁盘实际上没分配足够的数据副本,而剩余空间不足的磁盘取需要存储更多的数据副本,导致磁盘数据存储不均衡。
副本选择
为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本。如果在读取程序的同一个机架上有一个副本,那么就读取该副本。如果一个HDFS集群跨越多个数据中心,那么客户端也将首先读本地数据中心的副本。
安全模式
Namenode启动后会进入一个称为安全模式的特殊状态。 处于安全模式的Namenode是不会进行数据块的复制的。Namenode从所有的 Datanode接收心跳信号和块状态报告。块状态报告包括了某个Datanode所有的数据块列表。每个数据块都有一个指定的最小副本数。
当Namenode检测确认某个数据块的副本数目达到这个最小值(最小值默认是1,由dfs.namenode.replication.min属性设置),那么该数据块就会被认为是副本安全(safely replicated)的;在一定百分比(这个参数可配置,默认是0.999f,属性值为dfs.safemode.threshold.pct)的数据块被Namenode检测确认是安全之后(加上一个额外的30秒等待时间),Namenode将退出安全模式状态。接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他Datanode上。
如果datanode丢失的block达到一定的比例,namenode就会一直处于安全模式即只读模式。
当namenode处于安全模式时,该怎么处理?
找到问题所在,进行修复(比如修复宕机的datanode)。
或者可以手动强行退出安全模式(没有真正解决问题): hdfs namenode --safemode leave。
在hdfs集群正常冷启动时,namenode也会在safemode状态下维持相当长的一段时间,此时你不需要去理会,等待它自动退出安全模式即可。
用户可以通过dfsadmin -safemode value 来操作安全模式,参数value的说明如下:
enter - 进入安全模式
leave - 强制NameNode离开安全模式
get - 返回安全模式是否开启的信息
wait - 等待,在执行某条命令前先退出安全模式。
5、文件系统元数据的持久化
Namenode上保存着HDFS的名字空间。对于任何对文件系统元数据产生修改的操作,Namenode都会使用一种称为EditLog的事务日志记录下来。例如,在HDFS中创建一个文件,Namenode就会在Editlog中插入一条记录来表示;同样地,修改文件的副本系数也将往Editlog插入一条记录。Namenode在本地操作系统的文件系统中存储这个Editlog。
整个文件系统的名字空间,包括数据块到文件的映射、文件的属性等,都存储在一个称为FsImage的文件中,这个文件也是放在Namenode所在的本地文件系统上。
Namenode在内存中保存着整个文件系统的名字空间和文件数据块映射(Blockmap)的映像(即FsImage)。这个关键的元数据结构设计得很紧凑,因而一个有4G内存的Namenode足够支撑大量的文件和目录。
当Namenode启动时,或者检查点达到配置文件中的阀值,它从硬盘中读取Editlog和FsImage,将所有Editlog中的事务作用在内存中的FsImage上,并将这个新版本的FsImage从内存中保存到本地磁盘上,然后删除旧的Editlog,因为这个旧的Editlog的事务都已经作用在FsImage上了。这个过程称为一个检查点(checkpoint)。
hdfs dfsadmin -fetchImage fsimage.backup
//手动从namenode获取最新fsimage文件,并保存为本地文件。
因为编辑日志会无限增长,那么恢复编辑日志的过程就会比较长,解决方案是,运行辅助namenode,为主namenode内存中的文件系统元数据创建检查点。最终主namenode拥有最新的fsimage文件和更小的edits文件。
这也解释了辅助namenode和主namenode拥有相近内存需求的原因(辅助namenode也需要把fsimage文件载入内存)。
创建检查点的触发条件受两个配置参数控制,
dfs.namenode.checkpoint.period属性(辅助namenode每隔一段时间就创建检查点,单位s)。dfs.namenode.checkpoint.txns,如果从上一个检查点开始编辑日志大小达到多少的事务数时,创建检查点。
在主namenode发生故障时(假设没有备份),就可以从辅助的namenode上恢复数据。有两种实现方式。
方法一,将相关的存储目录复制到新的namenode中 。
方法二,使用-importCheckpoint选项启动namenode守护进程,从而将辅助namenode用作新的主namenode,有个前提时,dfs.namenode.dir属性定义的目录中没有元数据时。
6、通讯协议
所有的HDFS通讯协议都是建立在TCP/IP协议之上。客户端通过一个可配置的TCP端口连接到Namenode,通过ClientProtocol协议与Namenode交互。而Datanode使用DatanodeProtocol协议与Namenode交互。
一个远程过程调用(RPC)模型被抽象出来封装ClientProtocol和Datanodeprotocol协议。在设计上,Namenode不会主动发起RPC,而是响应来自客户端或 Datanode 的RPC请求。
7、健壮性
HDFS的主要目标就是即使在出错的情况下也要保证数据存储的可靠性。
常见的三种出错情况是:Namenode出错, Datanode出错和网络割裂(network partitions)。
心跳检测,磁盘数据错误和重新复制。
每个Datanode节点周期性地向Namenode发送心跳信号。网络割裂可能导致一部分Datanode跟Namenode失去联系。Namenode通过心跳信号的缺失来检测这一情况,并将这些近期不再发送心跳信号Datanode标记为宕机,不会再将新的IO请求发给它们。任何存储在宕机Datanode上的数据将不再有效。
Datanode的宕机可能会引起一些数据块的副本系数低于指定值,Namenode不断地检测这些需要复制的数据块,一旦发现就启动复制操作。
设置合适的datanode心跳超时时间,避免用datanode不稳定导致的复制风暴。
在下列情况下,也可能需要重新复制:某个Datanode节点失效,某个副本遭到损坏,Datanode上的硬盘错误,或者文件的副本系数增大。
集群均衡(针对datanode)
HDFS的架构支持数据均衡策略。如果某个Datanode节点上的空闲空间低于特定的临界点,按照均衡策略系统就会自动地将数据从这个Datanode移动到其他空闲的Datanode。
个文件的请求突然增加,那么也可能启动一个计划创建该文件新的副本,并且同时重新平衡集群中的其他数据。这个均衡策略目前还没有实现。
数据完整性(针对datanode)
从某个Datanode获取的数据块有可能是损坏的,损坏可能是由Datanode的存储设备错误、网络错误或者软件bug造成的。HDFS客户端软件实现了对HDFS文件内容的校验和(checksum)检查。
当客户端创建一个新的HDFS文件,会计算这个文件每个数据块的校验和,并将校验和作为一个单独的隐藏文件保存在同一个HDFS名字空间下。当客户端获取文件内容后,它会检验从Datanode获取的数据跟相应的校验和文件中的校验和是否匹配,如果不匹配,客户端可以选择从其他Datanode获取该数据块的副本。
元数据磁盘错误(针对namenode出错)
FsImage和Editlog是HDFS的核心数据结构。如果这些文件损坏了,整个HDFS实例都将失效。因而,Namenode可以配置成支持维护多个FsImage和Editlog的副本。任何对FsImage或者Editlog的修改,都将同步到它们的副本上。这种多副本的同步操作可能会降低Namenode每秒处理的名字空间事务数量。然而这个代价是可以接受的,因为即使HDFS的应用是数据密集的,它们也非元数据密集的。当Namenode重启的时候,它会选取最近的完整的FsImage和Editlog来使用。
另外一个可选方案是通过共享存储NFS或一个分布式编辑日志(也叫journal)实现多namenode节点(HA),来增强故障恢复能力。
在HDFS HA的实现中,配置了一对active-standby的namenode,当活动的namenode失效,备用的namenode就会接管它的任务并开始服务于客户端的请求。
实现HA需要在架构上做如下修改:
namenode之间通过高可用共享存储实现编辑日志的共享,当备用namenode接管工作之后,它将通读共享编辑日志直到末尾,实现与active namenode状态同步,并继续读取由活动namenode写入的新条目。
datanode需要同时向两个namenode发送数据块处理报告,因为数据块映射信息存在namenode的内存,而非硬盘。
客户端使用特定的机制处理namenode的失效,这一机制对于用户是透明的。
辅助namenode的角色被namenode所包含,备用namenode为活动的namenode命名空间设置周期性检查。
快照
快照支持某一特定时刻的数据的复制备份。利用快照,可以让HDFS在数据损坏时恢复到过去一个已知正确的时间点。
8、数据组织
数据块
HDFS被设计成支持大文件,适用HDFS的是那些需要处理大规模的数据集的应用。这些应用都是只写入数据一次,但却读取一次或多次,并且读取速度应能满足流式读取的需要。HDFS支持文件的“一次写入多次读取”语义。一个典型的数据块大小是128MB。因而,HDFS中的文件总是按照128M被切分成不同的块,每个块尽可能地存储于不同的Datanode中。
流水线复制
当客户端向HDFS文件写入数据的时候,一开始是写到本地临时文件中。假设该文件的副本系数设置为3,当本地临时文件累积到一个数据块的大小时,客户端会从Namenode获取一个Datanode列表用于存放副本。然后客户端开始向第一个Datanode传输数据,第一个Datanode一小部分一小部分(4 KB)地接收数据,将每一部分写入本地仓库,并同时传输该部分到列表中第二个Datanode节点。第二个Datanode也是这样,一小部分一小部分地接收数据,写入本地仓库,并同时传给第三个Datanode。最后,第三个Datanode接收数据并存储在本地。
因此,Datanode能流水线式地从前一个节点接收数据,并在同时转发给下一个节点,数据以流水线的方式从前一个Datanode复制到下一个。
9、可访问性
HDFS给应用提供了多种访问方式。用户可以通过Java API接口访问,也可以通过C语言的封装API访问,还可以通过浏览器的方式访问HDFS中的文件。通过WebDAV协议访问的方式正在开发中。
DFSShell
HDFS以文件和目录的形式组织用户数据。它提供了一个命令行的接口(DFSShell)让用户与HDFS中的数据进行交互。命令的语法和用户熟悉的其他shell(例如 bash, csh)工具类似。下面是一些动作/命令的示例:
DFSAdmin
DFSAdmin 命令用来管理HDFS集群。这些命令只有HDSF的管理员才能使用。下面是一些动作/命令的示例:
浏览器接口
一个典型的HDFS安装会在一个可配置的TCP端口开启一个Web服务器用于暴露HDFS的名字空间。用户可以用浏览器来浏览HDFS的名字空间和查看文件的内容。
10、存储空间回收
文件的删除和恢复
当垃圾回收生效时,通过fs shell删除的文件并没有立刻从HDFS中删除。实际上,HDFS会将这个文件重命名转移到user//.Trash目录。只要文件还在.Trash目录中,该文件就可以被迅速地恢复。文件在Trash中保存的时间是可配置的,当超过这个时间时,Namenode就会将该文件从名字空间中删除。删除文件会使得该文件相关的数据块被释放。注意,从用户删除文件到HDFS空闲空间的增加之间会有一定时间的延迟。
只要被删除的文件还在.Trash目录中,用户就可以恢复这个文件。如果用户想恢复被删除的文件,他/她可以浏览.Trash目录找回该文件。
减少副本系数
当一个文件的副本系数被减小后,Namenode会选择过剩的副本删除。下次心跳检测时会将该信息传递给Datanode。Datanode遂即移除相应的数据块,集群中的空闲空间加大。同样,在调用setReplication API结束和集群中空闲空间增加间会有一定的延迟。
【本文为数澜用户原创内容,转载时必须标注文章的来源,文章链接,文章作者等基本信息】