Hadoop组件学习(三)——HDFS的设计与高可用性
相关阅读:
HDFS的设计
大数据集:在HDFS上运行的应用程序具有大量数据集,因此HDFS支持大文件并具有较高的聚合数据带宽;HDFS中的典型文件大小为GB到TB
流式数据访问:HDFS支持一次写入、多次读取的访问模式;数据集通常由数据源生成或从数据源中复制而来,接着长时间在此数据集上进行各种分析。每次分析都将涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。
故障恢复:Hadoop并不需要运行在昂贵且高可靠的硬件上,是设计运行在商用硬件(在零售店可以买到的普通硬件)的集群上的,因此至少对于庞大的集群来说,节点故障的几率还是非常高的;因此HDFS具备检测故障并快速,自动的从故障中恢复而不会让用户察觉到明显的中断。
简单一致性模型:HDFS中的文件写入只支持单个写入者,而且写操作总是以“只添加”方式在文件末尾写数据,它不支持多个写入者的操作,也不支持在文件的任意位置进行修改的操作。此设计简化了数据一致性问题并实现了高吞吐量数据访问。
高度可配置:Hadoop用java编写,在所有主要平台上均受支持,默认配置适合许多安装方式;在大多数情况下,仅需要针对非常大的集群调整配置。
数据块
HDFS数据块的默认大小是128MB;HDFS上的文件当大于数据块大小时会被划分为多个块存储;同时HDFS中小于一个块大小的文件不会占据整个块的空间,假如,当一个1MB的文件存储在一个128MB的块中,文件只使用1MB的磁盘空间,而不是128MB。
HDFS块大小设计的比较大的目的是为了最小化寻址开销;当块足够大时,从磁盘传输数据的时间会明显大于定位定位这个块开始位置所需的时间,因而,传输一个由多个块组成的大文件的时间取决于磁盘的传输速率。但是这个参数也不能设置的过大,因为MapReduce中的map任务通常一次只处理一个块中的数据,如果任务数太少(少于集群中的节点数量),作业的运行速度就会比较慢。
HDFS对数据块进行抽象设计同时会带来许多好处:最明显的好处是一个文件的大小可以大于网络中任意一个磁盘的容量,文件的所有块并不需要存储在同一个磁盘上,因此它们可以利用集群上的任意一个磁盘进行存储。另外的好处就是大大简化了存储子系统的设计以及适用于数据备份进而提供数据容错能力和提高可用性。
NameNode和DataNode
HDFS集群由两类节点以管理节点-工作节点模式运行,即一个管理节点(NameNode)和多个工作节点(DataNode)。NameNode管理文件系统的命名空间,它维护着文件系统树以及整颗树内所有的文件和目录;这些信息以两个文件形式永久保存在本地磁盘上:命名空间镜像文件和编辑日志文件。NameNode也记录着每个文件中各个块所在的数据节点信息,但它并不永久保存块的位置信息,因为这些信息会在系统启动时根据数据节点信息重建。
DataNode是文件系统的工作节点;它们根据需要存储并检索数据块(受客户端或NameNode调度),并且定期向NameNode发送它们所存储的块的列表。
没有NameNode,文件系统将无法使用;如果运行NameNode服务的机器损坏,文件系统上所有的文件将会丢失,因为我们将不知道如何根据DataNode的块重建文件,因此,对NameNode实现容错是非常重要的;Hadoop为此提供了两种机制,在高可用性部分将进行详细的介绍。
联邦HDFS
NameNode在内存中保存文件系统中每个文件和每个数据块的引用关系,当对于一个拥有大量文件的超大集群来说,内存将成为限制系统横向扩展的瓶颈;因此在2.x的发行版本中引入了联邦HDFS,其允许系统通过添加NameNode实现扩展,其中每个NameNode管理文件系统命名空间中的一部分。例如,一个NameNode可能管理/user目录下的所有文件,而另一个NameNode可能管理/share目录下的所有文件
如上图所示,在联邦环境下,每个NameNode维护一个命名空间卷(namespace volume),由命名空间的元数据和一个数据块池(block pool)组成,数据块池包含该命名空间下文件的所有数据块;命名空间卷之间是相互独立的,两两之间并不相互通信,甚至其中一个NameNode的失效也不会影响由其他NameNdoe维护的命名空间的可用性;数据块池不在进行切分,因此集群中的DataNode需要注册到每个NameNode,并且存储着来自多个数据块池中的数据块。
联邦HDFS的主要好处在于命名空间可伸缩性,联合会添加命名空间水平伸缩,大型部署或使用大量小文件的部署可通过允许将更多的NameNode添加到集群中而实现命名空间的扩展;同时在性能上,文件系统的吞吐量不受单个NameNode的限制,扩展文件系统的读/写吞吐量;最后单个NameNode在多用户环境中可能因为其他应用程序过载而降低运行关键性应用程序的速度,而通过使用多个NameNode可以将不用类别的应用程序和用户隔离到不同的名称空间中。
联盟HDFS的配置
联盟会添加新的NameServiceId抽象;NameNode及其对应的辅助/备份/检查指针节点均属于NameServiceId;为了支持单个配置文件,NameNode和Secondary/backup/checkpointer配置参数以NameServiceId为后缀
步骤1:将dfs.nameservices参数添加到配置中,并用逗号分隔的NameServiceId列表进行配置;DataNode将使用它来确定集群中的NameNode。
步骤2:对于每个Namenode和Secondary NameNode / BackupNode / Checkpointer,将以下带有相应NameServiceId后缀的配置参数添加到公共配置文件中
进程 | 配置参数 |
---|---|
Namenode | dfs.namenode.rpc-address dfs.namenode.servicerpc-address dfs.namenode.http-address dfs.namenode.https-address dfs.namenode.keytab.file dfs.namenode.name.dir dfs.namenode.edits.dir dfs.namenode.checkpoint.dir dfs.namenode.checkpoint.edits.dir |
Secondary Namenode | dfs.namenode.secondary.http-address dfs.secondary.namenode.keytab.file |
BackupNode | dfs.namenode.backup.address dfs.secondary.namenode.keytab.file |
这是带有两个NameNodes的示例配置
数据块缓存
通常DataNode是从磁盘中读取数据块,但对于访问频繁的文件,其对于的数据块可能会被显式的缓存在DataNode的内存中,以堆外块缓存(off-heap block cache)的形式存在;默认情况下,一个数据块仅缓存一个DataNode的内存中;用户或者应用可以通过在缓冲池中添加一个cache directive来告诉NameNode需要缓存哪些文件以及保存多久。
HDFS的高可用性
上文提到没有NameNode或NameNode服务挂掉,文件系统将无法使用;Hadoop 2.x版本针对该问题增加了对HDFS高可用性(HA)的支持,通过配置一对活动-备用(active-standby)NameNode;当活动NameNode失效时,备用NameNode就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显中断;而实现这一目标需要在架构上做如下修改:
NameNode之间需要通过高可用共享存储实现编辑日志的共享;当备用NameNode接管工作后,它将通读共享编辑日志直至末尾,以实现与活动NameNode同步,并继续读取由活动NameNode写入的新条目。
DataNode需要同时向两个NameNode发送数据块处理报告,因为数据块的映射信息存储在NameNode的内存中,而非磁盘
客户端需要使用特定的机制来处理NameNode的失效问题,这一机制对用户时透明的
辅助NameNode的角色被备用NameNode所包含,备用NameNode为活动的NameNode命令空间设置周期性检查点。
可以从两种高可用性共享存储做出选择:NFS过滤器或群体日志管理器(QJM);QJM是一个专用的HDFS实现,为提供一个高可用的编辑日志而设计,被推荐用于大多数HDFS部署中。QJM以一组日志节点(journal node)的形式运行,每一次编辑必须写入多数日志节点;典型的,有三个日志节点,因此系统能够容忍其中任何一个的丢失;
在活动NameNode失效之后,备用NameNode能够快速实现任务接管,因为最新的状态时存储在内存中,但是实际观察到的失效时间略长一点,这是因为系统需要确定活动NameNode是否真的失效了
在活动NameNode失效且备用NameNode也失效的情况下,当然发生这类情况的概率时非常低的,管理员依旧可以声明一个备用NameNode并实现冷备份,通过配置DataNode和客户端以便使用这个新的NameNode,新的NameNode直到满足以下情形才能响应服务:(1) 将命名空间的映像导入内存中;(2) 重演编辑日志;(3) 接收到足够多的来自DataNode的数据块报告并退出安全模式。
故障切换与规避
系统中有一个称为故障转移控制器(failover controller)的新实体,管理着将活动NameNode转移为备用NameNode的转换过程;有多种故障转移控制器,但默认的一种使使用了Zookeeper来确保有且仅有一个活动NameNode。每一个NameNode运行着一个轻量级的故障转移控制器,其工作就是监视宿主NameNode是否失效(通过一个简单的心跳机制实现)并在NameNode失效时进行故障切换。
管理员也可以手动发起故障转移,例如在进行日常维护时;这称为“平稳的故障转移”(graceful failover),因为故障转移控制器可以组织两个NameNode有序的切换角色。
但在非平稳故障转移的情况下,无法确切知道失效NameNode是否已经停止运行。例如,在网速非常慢或者网络被分割的情况下,同样也可能激发故障转移,但是先前的活动NameNode依然运行着并且依旧是活动NameNode;高可用实现做了更进一步的的优化,以确保先前活动的NameNode不会执行危害系统并导致系统崩溃的操作,该方法称为“规避”(fencing)
同一时间QJM仅允许一个NameNode向编辑日志中写入数据。然而,对于先前的活动NameNode而言,仍有可能响应并处理客户过时的读请求,因此,设置一个SSH规避命令用于杀死NameNode的进程是一个好主意。当使用NFS过滤器实现共享编辑日志时,由于不可能同一时间只允许一个NameNode写入数据(这也是为什么推荐QJM的原因),因此需要更有力的规避方法,规避机制包括:撤销NameNode访问共享存储目录的权限、通过远程管理命令屏蔽相应的网络端口。诉诸的最后手段是,先前活动NameNode可以通过一个相当形象的称为“一枪爆头”(STONITH,shoot the other node in the head)的技术进行规避,该方法主要通过一个特定的供电单元对相应主机进行断电操作。
好文推荐: