@changedi
2017-02-04T20:48:27.000000Z
字数 8581
阅读 3447
大数据
HDFS
原文:http://hadoop.apache.org/docs/r2.6.4/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html
HDFS是个分布式文件系统,包含几个特点(区别于普通分布式文件系统):高容错
、高吞吐
。高容错可以使得系统部署在廉价硬件上,而高吞吐则非常适合做大规模数据集的应用。
硬件失效是常态而不是特例。一个HDFS集群可能包含了成百上千的服务器,每个都会存储文件系统的部分数据。而大量的组件就会导致组件出错的概率非常高,而这也意味着HDFS的部分组件会经常不工作。因此,检查缺陷和快速自动地恢复就成了HDFS的核心架构目标。
运行在HDFS上的应用程序需要流式访问数据集的能力。它们不是普通的运行在普通文件系统上的程序。HDFS被设计用来应对批量计算的场景,而不是用来和用户交互。重点是数据访问的高吞吐而不是低延迟。POSIX引入了大量的硬性需求来约束应用程序,而这些需求不是HDFS的目标需求。POSIX语义在一些关键领域被认为可以提高数据吞吐率。
运行在HDFS上的程序拥有大规模的数据集。一个HDFS文件可能是GB级别或是TB级别的存储。因此HDFS被调优为存储大文件。它应该提供高聚合的数据带宽并且可以在单个集群内扩展到其他的上百上千的节点。程序应该支持在单实例中存在千万级别的文件。
HDFS程序需要一个一次写入多次读出的文件访问模型。一旦一个文件被创建、写入数据然后关闭,这个文件应该不再需要被改动。此假设简化了数据一致性的问题,并且支持了数据的高吞吐。一个Map/Reduce程序或者一个网络爬虫程序就非常符合这种模型。未来有计划支持对于文件的追加写。
一个程序如果在运行计算任务时能更贴近其依赖的数据,那么计算会更高效。尤其是在数据集规模很大时该效应更加明显。因为这会最小化网络消耗而增加系统整体的吞吐能力。这一假设就是:把计算靠近数据要比把数据靠近计算成本更低。HDFS提供给应用程序接口来做到移动程序使其离数据更近。
HDFS被设计为可以很容易的从一个平台移植到另一个平台。这有利于推广HDFS,使其作为广泛首选的大数据集应用的平台。
HDFS是一个主从master/slave架构。一个HDFS集群包含一个NameNode,这是一个master服务器,用来管理文件系统的命名空间以及调节客户端对文件的访问。除此,会有一堆DataNode,通常是集群里每个节点一个DataNode,管理在此节点上的数据存储。HDFS对外暴露一个文件系统命名空间,并允许用户数据以文件的形式存储。在内部,一个文件被分成一个或多个块并且这些块被存储在一组DataNode上。NameNode来执行文件系统命名空间的操作比如打开、关闭、重命名文件和目录。NameNode同时也负责决策将数据块映射到对应的DataNode。而DataNode负责服务文件系统客户端发出的读写请求。DataNode同时也负责接受NameNode的指令来进行数据块的创建、删除和复制。
NameNode和DataNode都是被设计为在普通PC机上运行的软件程序。这些机器最典型的就是运行在一个GNU/Linux操作系统上。HDFS是Java写的;任何支持Java的机器都可以运行NameNode或者DataNode。Java语言本身的可移植性意味着HDFS可以被广泛的部署在不同的机器上。一个典型的部署就是一台专用机器来运行NameNode。集群中的其他机器每台运行一个DataNode实例。该架构并不排除在同一台机器上运行多个DataNode实例,但在实际的部署中很少的情况下会这么做。
单一NameNode的设计极大的简化了集群的系统架构。NameNode是所有HDFS元数据的仲裁和存储库。系统被设计为用户数据从来不会流经NameNode。
HDFS支持传统的分层文件组织。用户或者应用程序可以创建目录,并且在目中存储文件。文件系统命名空间层级与大多数文件系统都类似;用户可以创建和移除文件,在目录间移动文件,或者重命名一个文件。HDFS还没有实现用户配额或者访问权限。HDFS不支持硬链接或者软连接。当然HDFS不排除会支持这些feature。
NameNode负责维护文件系统命名空间。任何对文件系统命名空间或者其属性的改动都会被NameNode记录。应用程序可以声明HDFS文件的副本数。一个文件的副本数被称为该文件的复制因子。这部分信息由NameNode存储。
HDFS被设计来跨集群、跨机器地可靠地存储海量文件。它把每个文件存储为一系列的block;除了最后一个block,一个文件的所有block都是相同大小的。为了容错,一个文件的block会被复制。对于每个文件来说,block大小和复制因子都是可配置的。应用程序可以声明一个文件的副本数。复制因子可以在文件创建时声明,也可以在后来被修改。HDFS上的文件都是write-once的,并且在任何时刻都是严格保证只有一个writer来写入。
NameNode来控制所有的block的复制决策。它周期性地从集群中的DataNode收集心跳和block报告。收集到心跳则意味着DataNode正在提供服务。收集到block报告则包含了一个DataNode上的所有block列表。
副本的放置对于HDFS的可靠性和性能来说至关重要。对于副本放置的优化是HDFS区别于其他分布式文件系统的主要方面。这个特性的得来,需要大量的调优和实践。rack-aware的副本放置策略的目的是提升数据可靠性、可用性和网络带宽利用率。当前对副本放置策略的实现是朝这个方向的第一次努力。短期的目标是在生产系统校验,学习更多的行为以及构建一个测试和研究复杂策略的基础。
运行于集群中的大型HDFS实例一般都跨越多个机架(rack)。不同rack之间的两个节点通讯需要通过交换机。大多数情况下,同一rack内的机器间网络带宽要比不同rack之间的大。
NameNode决定每个DataNode的rack id,而这一过程在Hadoop的集群安装中有提到。一个简单的未经优化的策略就是将副本放置到各自独立的rack。这样可以避免在整个rack不可用时的数据丢失,同时可以充分利用多个rack在读取数据时的带宽。这一策略将副本均匀地分布在集群中使得在组件失效时可以轻松的做均衡负载。当然,这个策略也增加了写时消耗,因为一次写入就需要在不同rack之间传输block。
通用场景下,当复制因子是3时,HDFS的放置策略是将一个副本放置到本地rack的一个节点上,另一个在本地rack的不同节点上,最后一个放在不同rack的不同节点上。这一策略减少了rack之间的写操作从而提升了写性能。rack不可用的概率要比node不可用的概率低很多;这一策略并不影响数据可靠性和可用性。但是这一策略也确实减少了读取数据时的聚合网络带宽,毕竟一个block 数据是放置在两个不同的rack下而不是三个。这一策略没有均匀地分布副本。其中三分之一的副本在一个节点上,三分之二的副本在一个rack上,另三分之一的副本均匀分布在其他rack上。这一策略提高了写入性能而没有影响数据可靠性或者读取性能。
这里所描述的当前的默认副本放置策略是一项正在进行的工作。
为了最小化全局带宽消耗和读取延迟,HDFS试图满足从距离reader最近的副本读取数据的请求。如果在reader节点相同的rack内有副本存在,则由该副本提供数据满足读请求。如果HDFS集群跨越多个数据中心,那么本地数据中心的副本要优先于远程副本。
启动时,NameNode会进入一个特殊的状态叫做安全模式。当NameNode进入安全模式时,数据block不会进行复制。NameNode会接收DataNode的心跳和block报告消息。block报告包含了DataNode所负责的数据block列表。每个block有一个特定的最小副本数。当数据block的最小副本数通过NameNode检查后,一个block才被认为是安全复制。NameNode检查了一定比例的(配置过的)安全复制block后(额外再加30秒),NameNode退出安全模式。然后继续确定那些有比指定副本数还少的数据block列表(如果有的话)。NameNode然后复制这些block到其他的DataNode。
HDFS的命名空间是存储在NameNode上的。NameNode使用一个叫做EditLog的事务日志来记录发生在文件系统元数据上的每一个变更。举个例子,在HDFS上创建一个新文件会导致NameNode插入EditLog一条新纪录。类似地,改变一个文件的复制因子也会导致插入EditLog一条记录。NameNode在其本地操作系统的文件系统里用一个文件来存储EditLog。整个文件系统的命名空间——包含block和文件和文件系统属性的映射——被存储在叫做FsImage的文件里。FsImage也是NameNode本地系统中的一个问价。
NameNode在内存里保持了一个包含整个文件系统命名空间和Block映射的文件镜像。这个关键的元数据项是可压缩的,一个4GB内存的NameNode可以支持海量的文件和目录。当NameNode启动时,它会从磁盘读取FsImage和EditLog,然后将EditLog的所有事务应用到内存中的FsImage,然后将新的FsImage刷盘回新的FsImage磁盘文件。然后它会删掉老的EditLog并建立一个新的,因为老的事务已经被apply到FsImage。这一过程叫做一个checkpoint。在当前的实现中,checkpoint只在NameNode启动时发生。周期性的checkpoint在近期的工作计划中。
DataNode在其本地文件系统中以文件的形式存储了HDFS的数据。DataNode对于HDFS文件无概念。它将HDFS数据的每个block存储到本地文件系统的独立的文件中。DataNode不会再同一个目录下创建所有的文件。相反,它使用启发式确定每个目录中的文件的最佳数量并适当地创建子目录。在本地文件系统中的一个目录下创建所有的文件并不是最佳策略,因为本地文件系统可能并不能高效的支撑单一目录下的大量文件存储。当DataNode启动时,它会扫描本地文件系统内,生成一个HDFS数据block和本地文件的映射的列表,然后将此报告发送给NameNode:这就是Block报告。
所有的HDFS通讯协议都是基于TCP/IP协议之上的。一个客户端创建一个连接,连接到NameNode机器对应配置的TCP端口。他们基于客户端协议来与NameNode通讯。DataNode使用DataNode协议与NameNode通讯。一个远程过程调用(RPC)同时抽象封装了客户端协议和DataNode协议。设计上,NameNode从不发起任何的RPC调用。相反,NameNode只响应从DataNode或者客户端发起的RPC请求。
HDFS的首要目标是可靠地存储数据,就算发生失败也仍然有效。三种常见类型的失效包括:NameNode失效,DataNode失效和网络分区。
每个DataNode会周期性地发送心跳信息给NameNode。一次网络分区会导致一组DataNode与NameNode丢失连接。NameNode会通过心跳缺失而检测到这次连接的丢失。NameNode会把这些检测不到心跳的DataNode标记为“死亡”,然后不再发送任何新的IO请求到这些DataNode。任何已经注册到“死亡”的DataNode上的数据对HDFS不再可用。DataNode的死亡可能会导致某些block的复制因子低于其指定值。NameNode持续地追踪着需要被复制的block,并在其需要时进行复制。重新复制的必要性可能在于以下原因:一个的DataNode可能变得不可用,副本可能损坏,一个的DataNode硬盘可能失败,或一个文件的复制因子可能增加。
HDFS的架构与数据重新均衡方案兼容。一种均衡方案是在一个DataNode可用空间低于一定阈值时,会自动将数据从DataNode搬运到另一个DataNode。在某个特定文件突然的高需求情况下,一种方案可能动态地创建额外副本,并在集群中均衡其他数据。这些数据重新均衡的方案都还没有被实现。
从一个DataNode获取的block数据很可能到达时崩溃。发生这种崩溃是因为存储设备出错,网络出错或者软件bug。HDFS客户端软件实现了校验HDFS文件的checksum的功能。当客户端创建了一个HDFS文件,它会为这个文件的每个block计算一个checksum,并将这些checksum存储到同名的HDFS命名空间下的一个独立的隐藏文件。当客户端拉取文件内容时,它会校验从DataNode拉取到的数据与其关联的checksum文件的checksum是否一致。如果不一致,那么客户端可以再尝试从其他的拥有该block副本的DataNode拉取数据。
FsImage和EditLog是HDFS的核心数据结构。这些文件的崩溃会导致HDFS整个实例的不可用。基于此原因,NameNode需要被配置为可以支持存储FsImage和EditLog的多份副本。任何对于FsImage或者EditLog的改动都会同步更新到每个FsImage和EditLog的副本。这种同步更新会降低NameNode可支持处理的命名空间事务速率。但是,这种降低是可接受的,因为即使HDFS程序是数据密集的,但是元数据不是数据密集的。当一个NameNode重启时,它会选择最新的一致的FsImage和EditLog来使用。
NameNode机器是HDFS集群的故障单点。如果NameNode机器挂了,那么人工干预是必要的。当前情况下,NameNode的自动重启和failover到其他机器还没有被支持。
快照支持在特定的一个时刻存储一份数据的副本。用途之一是用来在HDFS实例崩溃时可以回滚到之前某个时刻存储的版本。HDFS当前不支持快照,但是在未来会支持。
HDFS被设计来存储大文件呢。与HDFS兼容的程序都是处理大型数据集的。这些程序只进行一次写数据,但是会多次读取这些数据,而且是以流式读取。HDFS支持write-once-read-many的文件语义。一个HDFS典型的数据块大小是64 MB。因此HDFS文件被裁减为很多个64MB大小的块,如果可能的话,每个块都分布在不同的DataNode上。
客户端的创建文件请求部署马上到达NameNode的。事实上,HDFS客户端会将数据缓存到一个本地临时文件里。应用程序透明地将数据重定向写入到这个临时文件。当本地文件累积数据达到HDFS块大小,客户端才会连接NameNode。NameNode然后会将文件名写入文件系统命名空间并分配一个数据块来存储这些数据。NameNode会返回给客户端一个响应,其中包含DataNode的id和目标数据块的id。然后客户端将数据块从本地临时文件刷新到对应DataNode。当一个文件关闭后,本地临时文件中剩余的未刷新的数据会被传输到DataNode。然后客户端会通知NameNode文件关闭。此时此刻,NameNode会提交一个创建文件的操作到持久存储。如果NameNode在文件关闭前崩溃了,那么文件就丢失了。
在认真评估考虑HDFS的目标程序后,上面提到的方法被认可并接受。这些程序需要流式地写文件。一个客户端不做任何客户端buffer直接写远程文件的话,网络速度和带宽会影响吞吐。这种方法并非没有先例。之前的分布式文件系统比如AFS就曾经在客户端侧添加缓存来提升性能。POSIX规范已经被放宽从而可以获取更高的数据上传性能。
当客户端正在往HDFS文件中写数据时,数据首先会写入一个本地文件(如之前所述)。假设HDFS文件的复制因子是3。当本地文件积累用户数据满足一个完整block时,客户端从NameNode拉取一个DataNode的列表。这个列表包含了可以承载该数据块一个数据副本的DataNode。客户端然后将数据块刷新到第一个DataNode。第一个DataNode接着开始小部分地接收数据,然后将每部分写入到它的本地库,并且将该部分数据传输到列表中第二个DataNode。第二个DataNode开始接收该数据块的数据,并将该部分数据写入它的本地库然后将该部分数据刷新到第三个DataNode。最终,第三个DataNode将数据写入其本地库。这样,一个DataNode可以从前一个DataNode接收数据,以流水线的方式在同一时刻将数据转发到流水线中的下一个DataNode。因此数据是以流水线的方式从一个DataNode传输到下一个。
HDFS可以被应用程序以多种方式来访问。HDFS提供给应用程序一种原生的文件系统Java API。一个基于此Java API的C语言封装也是可用的。额外的情况下,HTTP浏览器也可以用来浏览HDFS实例的文件。通过WebDAV协议暴露HDFS的工作仍在进行中。
HDFS允许用户数据以文件和目录的形式组织。它提供了一个叫做FS shell的命令行界面来作为用户与HDFS数据交互的中介。该命令行的命令语法与其他的大家广为熟悉的文件系统shell类似(比如bash和csh)。这里列出一些行为和命令的对应:
Action | Command |
---|---|
创建个叫做/foodir的目录 | bin/hadoop dfs -mkdir /foodir |
删除/foodir的目录 | bin/hadoop dfs -rmr /foodir |
查看/foodir/myfile.txt文件的内容 | bin/hadoop dfs -cat /foodir/myfile.txt |
FS shell是给那些需要用脚本语言与数据交互的应用程序使用的。
DFSAdmin命令用于管理一个HDFS集群。这些指令只可以由HDFS管理员使用。这里列出一些行为和命令的对应:
Action | Command |
---|---|
设置集群为安全模式 | bin/hadoop dfsadmin -safemode enter |
生成DataNode的列表 | bin/hadoop dfsadmin -report |
Recommission or decommission DataNode(s) | bin/hadoop dfsadmin -refreshNodes |
一个典型的HDFS安装会配置一个web server透过一个配置的TCP端口来暴露HDFS命名空间。以此来允许用户通过使用web浏览器来浏览HDFS命名空间和查看文件内容。
当一个文件被用户或者应用程序删除时,它并没有立刻被HDFS移除。相反,HDFS会首先将其重命名然后移入一个/trash的目录。在/trash里的文件可以快速的被恢复使用。一个文件在/trash里维持的时间由一个配置来决定。当在/trash里过期后,NameNode从HDFS命名空间删除这个文件。删除文件会关联其对应的数据块的空间释放。注意在用户删除文件和对应的HDFS空间释放之间有个时间的延迟。
用户可以取消删除一个文件,只要它还在/trash目录下。如果一个用户希望取消删除一个已经删除了的文件,那么他可以浏览/trash目录并找到对应的文件。/trash目录只包含被删除文件的最细副本。/trash目录与其他目录一样,唯独的不同在于:HDFS启用了特殊的策略来自动删除该目录下的文件。当前默认的回收间隔设置是0(不在回收站保留,直接删除)。这个参数是可以配置的,配置项在core-site.xml里的fs.trash.interval。
当一个文件的复制因子被降低,NameNode就可以选择多余的可被删除的副本。下一次的心跳信息会传输该信息给DataNode。DataNode会删除对应的block,释放相应的空间。与之前的延迟类似,在setReplication API调用结束后到实际空间释放之间存在一个延迟。