HDFS详解
- hadoop1.x的HDFS默认块大小为64MB;hadoop2.x的默认块大小为128MB,最小化寻址开销。(配置项为
hdfs-site.xml
中的dfs.block.size
)
- 减少搜寻时间,一般硬盘传输速率比寻道时间要快,大的块可以减少寻道时间;
- 减少管理块的数据开销,每个块都需要在NameNode上有对应的记录;
- 对数据块进行读写,减少建立网络的连接成本;
- 减轻了namenode的压力
- 原因是hadoop集群在启动的时候,datanode会上报自己的block的信息给namenode。namenode把这些信息放到内存中。那么如果块变大了,那么namenode的记录的信息相对减少,所以namenode就有更多的内存去做的别的事情,使得整个集群的性能增强
hdfs-site.xml
中的dfs.relication
,默认为3,有2份冗余。- 使用块的好处
- 可以保存比单一磁盘大的文件:块的设计实际上是对文件进行分片,分片可以保存在集群的任意节点,从而使文件存储跨越了磁盘甚至机器的限制;
- 简化存储子系统:将存储子系统控制单元设置为块,可简化存储管理,并且也实现了元数据和数据的分开管理和存储;
- 容错性高:如果将
dfs.relication
设置为2,那么任意一个块损坏,都不会影响数据的完整性,用户在读取的时候,并不会察觉到异常。之后集群会将损坏的块的副本从其他候选结点复制到集群中能正常工作的节点,从而使副本数回到配置的水平。
- NameNode节点
- HDFS主从架构(master/slave)架构的主角色的扮演者;
- 维护整个文件系统的目录树,以及目录树里所有的文件和目录,这些信息以两种文件存储在本地文件中:
- 一种是文件系统镜像(File System Image, FSImage),即HDFS元数据的完整快照,每次NameNode启动的时候,默认会加载最新的命名空间镜像;
- 还有一种是命名空间镜像的编辑日志(Edit Log)。
- SecondaryNameNode节点
- 第二名字节点;
- 用于定期合并命名空间镜像和命名空间镜像编辑日志的辅助守护进程;
- 每个HDFS集群都有一个SecondaryNameNode,在生产环境下,一般SecondaryNameNode也会单独运行在一台服务器上。
- FSImage文件
- 文件系统元数据的一个永久性检查点。
- 并非每一个写操作都会更新到这个文件,因为FSImage是一个大型文件,如果频繁执行写操作,会使系统运行缓慢,解决方案:
- NameNode只将改动内容预写日志(WAL),即写入Edit Log。随着时间推移,Edit Log会越来越大,如果发生故障,会花费非常多的时间来回滚,因此需要定期合并FSImage和EditLog。
- 如果由NameNode来进行合并,那么NameNode在为集群提供服务时可能无法提供足够的资源,因此由SecondaryNameNode来进行合并。
- SecondaryNameNode引导NameNode滚动更新Edit Log,并将新的内容写入Edit Log.new;
- SecondaryNameNode将NameNode的FSImage和Edit Log文件复制到本地的检查点目录;
- SecondaryNameNode载入FSImage文件,回放Edit Log,将其合并到FSImage,将新的FSImage文件压缩后写入磁盘;
- SecondaryNameNode将新的FSImage文件送回到NameNode,NameNode在接收新的FSImage后,直接加载和应用该文件;
- NameNode将Edit Log.new更名为Edit Log。
- 默认情况下,该过程每小时发生一次,或者NameNode的Edit Log达到默认的64MB也会触发。
SecondaryName不是NameNode的“热备”。
- DataNode
- HDFS主从架构的从角色的扮演者。
- 在NameNode的指导下完成I/O任务。所有的块都存放于DataNode节点。对于DataNode所在的节点来说,块就是一个普通的文件。
- DataNode不断地向NameNode报告。初始化时,每个DataNode将当前存储的块告知NameNode,在集群正常工作时,DataNode仍然会不断地更新NameNode,为之提供本地修改的相关信息,同时接受来自NameNode的指令,创建、移动或者删除本地磁盘上的数据块。
- HDFS容错
- 心跳机制
- NameNode和DataNode之间维持心跳检测,当由于网络故障之类的原因,导致DataNode发出的心跳包没有被NameNode正常收到的时候,NameNode就不会将任何新的I/O操作派发给这个DataNode,该DataNode上的数据被认为是无效的,之后NameNode会检测是否有文件块的副本数目小于设置值,如果小于就自动复制新的副本到其他DataNdoe节点。
- 检测文件快的完整性
- HDFS会记录每个新创建文件的所有块的校验和。当以后检索这些文件时或者从某个节点获取块时,会首先确认校验和是否一致,如果不一致,会从其他DataNode节点上获取该块的副本。
- 集群的负载均衡
- 由于节点的失效或者增加,可能导致数据分布不均匀,当某个DataNode节点的空闲空间大于一个临界值的时候,HDFS会自动从其他DataNode迁移数据过来。
- NameNode上的FSImage和Edit Log文件
- NameNode上的FSImage和Edit Log文件时HDFS的核心数据结构,如果这些文件损坏了,HDFS将失效。因此,NameNode由SecondaryNameNode定期备份这两个文件,NameNode在Hadoop中存在单点故障的可能,当NameNode出现机器故障,手动干预是必须的。
- 文件的删除
- 删除并不是马上从NameNode一处命名空间,而是存放在/trash目录随时可以恢复,知道超过设置时间才被正式移除。设置时间由
hdfs-site.xml
文件的配置项fs.trash.interval
决定,单位为秒。
- 删除并不是马上从NameNode一处命名空间,而是存放在/trash目录随时可以恢复,知道超过设置时间才被正式移除。设置时间由
- 心跳机制
- 块分布
- Hadoop的默认布局是在HDFS客户端节点上放第一个副本,但是由于HDFS客户端能运行于集群之外,就随机选择一个节点,Hadoop会尽量避免选择那些存储太满或者太忙的节点。
- 第二个副本放在与第一个不同且随机另外选择的机架中的节点上。
- 第三个副本与第二个副本放在相同的机架,且随机选择另外一个节点。
- 其他副本放在集群随机选择的节点上,Hadoop也会避免在相同机架上放太多副本。
- 当NameNode按照上面的策略选定副本存储的位置后,就会根据集群网络拓补图创建一个管道。
- 提供很好的数据冗余性(如果可能,块存储在两个机甲中),实现了很好的负载均衡,包括写入带宽(写入操作只需要遍历一个交换机)、读取性能(从两个机甲中进行选择读取)和集群中块的均匀分布。
- 数据读取
- HDFS客户端首先访问NameNode,并告诉它所要读取的文件。
- 读取之前,HDFS对客户的身份信息进行验证:
- 第一种方式通过信任的客户端,由其指定用户名;
- 第二种方式通过诸如Kerberos等强制验证机制完成。
- 当文件存在且用户有访问权限,NameNode告诉HDFS客户端这个文件的第一个数据块的标号以及保存有该数据块的DataNode列表(这个列表是DataNode与HDFS客户端间的距离进行了排序)。
- HDFS访问最合适的DataNode,读取所需的数据块。
- 重复进行直到所有数据块读取完成或HDFS主动关闭文件流。
如果HDFS客户端是集群中的DataNode,该节点从本地DataNode中读取数据。
- 数据写入
- HDFS客户端通过HDFS相关API发送请求,打开一个要写入的文件,如果用户有写入文件的权限,请求被送达NameNode,并建立该文件的元数据(此时新建立的文件元数据并未和任何数据块相关联)。
- HDFS客户端收到“打开文件成功”的响应,接着开始写入数据。当客户端将数据写入流时,数据会被自动拆分成数据包,并将数据包保存在内存队列中(客户端有一个独立的进程,它从队列中读取数据包,并向NameNode请求一组DataNode列表,以便写下一个数据块的多个副本)。
- HDFS客户端直接连接到列表中的第一个DataNode,该DataNode又连接到第二个DataNode,第二个连接到第三个,建立数据块的复制管道。
- 复制管道中的每一个DataNode都会确认所收到的数据包已经成功写入磁盘。HDFS客户端应用程序维护着一个列表,记录着哪些数据包尚未收到确认信息。每收到一个响应,客户端便知道数据已经成功地写入管道中的一个DataNode。
- 当数据块被写入列表中的DataNode中,HDFS客户端将重新向NameNode申请下一组DataNode。
- 最终,客户端将剩余数据包写入全部磁盘,关闭数据管道并通知NameNode文件写操作已经完成。
- 如果写入的时候,复制管道中的某一个DataNode无法将数据写入磁盘
- 管道立即关闭,已发送的但尚未收到确认的数据包会退回到队列中,以确保管道中错误节点的下游节点可以得到数据包。
- 剩下的健康的DataNode中,正在写入的数据块会被分配新的blk_id。
- 当发生故障的数据节点恢复后,冗余的数据块机会因为不属于任何文件而被自动丢弃,由剩余DataNode节点组成的新复制管道会重新开放,写入操作得以继续,写操作将继续直至文件关闭。
- NameNode如果发现文件的某个数据块正在通过复制管道进行复制,就会异步地创建一个新的复制块,这样,即便多个DataNode发生错误,HDFS客户端仍然可以从数据块的副本中恢复数据(前提是满足最少数目要求的数据副本
dfs.replication.min
已经被正确写入)。
- 联邦HDFS
- 对于一个拥有大量文件的超大集群来说,内存可能会出现瓶颈,2.x版本引入联邦HDFS允许系统通过添加NameNode来实现扩展,其中每个namenode管理文件系统命名空间中的一部分。例如,一个namenod可能管理/user目录下的所有文件,而另一个namenode可能管理/share目录下的所有文件。
- 在联邦环境下,每个namenode维护一个命名空间卷,由命名空间的元数据和一个数据块池组成,数据块池包含该命名空间下文件的所有数据块。命名空间卷之间相互独立,不互相通信,甚至齐总一个namenode失效也不会影响其他namenode维护的命名空间的可用性。数据块池不再进行切分,因此集群中的datanode需要注册到每个namenode,并且存储着来自多个数据块池的数据块。
- 想要访问联邦HDFS集群,客户端需要使用客户端挂载数据表将文件路径映射到namenode。通过ViewFileSystem和viewfs://URI进行配置管理。
- 单点失效问题
- 通过联合使用在多个文件系统中备份namenode的元数据和通过备用namenode创建监测点能防止数据丢失,但是依旧无法实现文件系统的HA。因为namenode是唯一存储元数据与文件到数据块映射的地方。
- 从一个失效的namenode恢复,必须启动一个拥有文件系统元数据副本的新的namenode,并配置datanode和客户端以便使用这个新的namenode:
- 将命名空间的映像导入内存
- 重演编辑日志
- 接收到足够多的来自datanode的数据块报告并退出安全模式
- 高可用(HA)
- Hadoop2增加了对HDFS的HA支持。配置一对活动-备用(active-standby)namenode。当活动namenode失效,备用namenode就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显中断:
- namenode之间需要通过高可用共享存储实现编辑日志的共享。当备用namenode接管工作之后,它将通读共享编辑日志直至末尾,以实现与活动namenode的状态同步,并继续读取由活动namenode写入的新条目。
- datanode需要同时向两个namenode发送数据块处理报告,因为数据块的映射信息存储在namenode内存中,而非硬盘。
- 客户端需要使用特定的机制来处理namenode的失效问题,这一机制对用户是透明地。
- 辅助namenode的角色被备用namenode所包含,备用namenode为活动namenode命名空间设置周期性检查点。
- 两种高可用共享存储:NFS过滤器或群体日志管理器(QJM):
- QJM是一个专用的HDFS实现,为提供一个高可用的编辑日志而设计。QJM以一组日志节点的形式运行,每一次编辑必须写入多数日志节点。(QJM的实现并没有用到ZooKeeper,HDFS HA在选取活动namenode的时候确实使用了ZooKeeper)
- Hadoop2增加了对HDFS的HA支持。配置一对活动-备用(active-standby)namenode。当活动namenode失效,备用namenode就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显中断: