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的目录结构 ![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/214098fe819dc20b89dd8055d4db54ad.png#pic_center)
(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需要缓存哪些文件及存多久。缓存池是一个用于管理缓存权限和资源使用的管理性分组。
提到好几次块这个词了&#