1 HDFS 基础
1.1 NameNode
- 文件 fsimage:保存文件系统目录树信息以及文件与块的对应关系,每次namenode启动的时候,都会读取fsimage,将目录树信息装载到内存中。
- 文件 edits.log 日志:储存日志信息。在namenode启动后,所有对目录结构的增加,删除,修改等操作都会记录到edits.log文件中,不会同步记录到 fsimage。
1.2 DataNode
- DataNode 存储数据按block 为单位读写的。block是hdfs读写数据的基本单位。大小从2.7.3版本开始后为128m。寻址时间(假设12ms)为传输时间(1.2秒)的1%为最佳,目前传输速率为100MB/s,传输大概(120M)。
- block为逻辑概念不真正存储数据,只是划分文件,一般副本为3个
1.3 Secondary NameNode
工作流程: SecondaryNameNode 询问NameNode是否需要checkpoint。直接带回NameNode是否检查结果 , SecondaryNameNode请求执行checkpoint。 NameNode滚动正在写的edits.log日志 , 将滚动前的编辑日志和镜像文件拷贝到SecondaryNameNode , SecondaryNameNode加载编辑日志和镜像文件到内存,并合并。 生成新的镜像文件fsimage.chkpoint , 拷贝fsimage.chkpoint到NameNode, NameNode将fsimage.chkpoint重新命名成 fsimage 。主要是解决NameNode启动的时候,edits.log过大需要合并太久问题。
2 HDFS的读写流程
2.1 读流程:
HDFS 调用 open() 返回 FileSystem 调用 open() 返回 distributedFilesystem 调用 DFSInputStream() 返回DFSInputStream 调用openInfo() 调用NameNodeRpcServer 获取到block位置,将block信息写入输出流,保存到DFSInputStream输入流对象中的成员变量中,交给IOUtil,下载文件到本地
2.2 写流程:
(源码可以参照读流程自己看看)
HDFS client 调用文件系统(distributed filesystem)的 create 方法,远程调用 NameNode 中的 create 方法。此时 NameNode 进行4个操作检测自己是否正常,文件是否存在,客户端权限与写日志。此时create的返回值为FSData outputstream对象,此时 HDFS client 调用 write 方法和 NameNode 进行连接,调用 addblock 方法返回 block 分配的 Datanode 列表
主要放在 DFSOutputStream.class 先写入 writeChunk (32768)后 queueCurrentPacket (),置入 createBlockOutputStream , 向DataNode传输,DataNode传输结束会检测checkSum,成功就删除 ack queue 的 package,否则放回 data queue 重传, 结束后关闭流,告诉NameNode,调用complete方法结束
2.3 疑点:
读取数据的完整性:DataNode的存储的除了数据还附带checkSum检验(CRC32算法 32位4个字节大小) , 每次读取完数据后,都会对数据做一次checkSum对比,保证数据传输的完整性
3 NameNode HA 高可用实现
3.1 解决单点故障问题
- 使用两台NameNode互备,Active NN 与 Standby NN 形成互备。某时间段都只有一个台NameNode状态为Active,一台为Standby
- ZKFailoverController 作为独立的进程运行,对 NameNode 的主备切换进行总体控制。ZKFailoverController 能及时检测到 NameNode 的健康状况,在主 NameNode 故障时借助 Zookeeper 实现自动的主备选举和切换,当然 NameNode 目前也支持不依赖于 Zookeeper 的手动主备切换。
3.2 数据同步共享问题
- 共享存储系统是实现 NameNode 的高可用最为关键的部分,共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。Active NameNode 和 Standby NameNode 通过共享存储系统实现元数据同步。在进行主备切换的时候,新的主 NameNode 在确认元数据完全同步之后才能继续对外提供服务。
- 基于 QJM 的共享存储系统主要用于保存 EditLog,并不保存 FSImage 文件。FSImage 文件还是在 NameNode 的本地磁盘上。QJM 共享存储的基本思想来自于 Paxos 算法,采用多个称为 JournalNode 的节点组成的 JournalNode 集群来存储 EditLog。每个 JournalNode 保存同样的 EditLog 副本。每次 NameNode 写 EditLog 的时候,除了向本地磁盘写入 EditLog 之外,也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求,只要大多数 (majority) 的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode,那么根据大多数的原则,最多可以容忍有 N 台 JournalNode 节点挂掉。
4 NameNode 局限 和 Federation 架构设计
4.1 局限:
1. 命名空间的限制,NameNode 上存储着整个 HDFS 上的文件的元数据,NameNode 是部署在一台机器上的,因为单个机器硬件的限制,必然会限制 NameNode 所能管理的文件个数,制约了数据量的增长。
2. 数据隔离问题, 整个 HDFS 上的文件都由一个 NameNode 管理,所以一个程序很有可能会影响到整个 HDFS 上的程序,并且权限控制比较复杂。
3. 性能瓶颈, 单个NameNode 时 HDFS文件系统的吞吐量受限于单个 NameNode 的吞吐量。因为 NameNode 是个 JVM 进程,JVM 进程所占用的内存很大时,性能会下降很多。
4.2 方案Federation架构
Federation 中的多个 NameNode 是不同的,可以理解为将一个 NameNode 切分为了多个 NameNode,每一个 NameNode 只负责管理一部分数据。HDFS Federation 中的多个 NameNode 共用 DataNode。
5 补充:
5.1 HDFS快照
快照是一个只读的基于时间点文件系统拷贝,是整个文件系统的也可以是一部分,用于数据备份,防止用户错误操作和容灾恢复,snapshot 并不影响hdfs的正常操作,修改会按照时间的反序记录,是当前数据减去修改的部分计算出来的,快照会存储在snapshottable的目录下
5.2 namenode 宕机后的恢复
secondaryNamenode 通过http get 方式把edit.log 和 fsimage信息拉取过来 , 合并产生新文件fsimage.ckpt,再回传给namenode。而客户端对namenode的操作也会产生新日志,单独放在edits.new 文件中。传回fsimage.ckpt会被分解成fsimage edits.log,edits.log和合edits.new的日志文件一同合并作为一份完整的edits.log文件
5.3 hdfs 心跳机制
1 namenode 全权管理数据块的复制,周期性从集群中的每个dataname接收心跳信号和块状态报告,接收心跳信号意味着该datanode工作状态正常,块状态包含datanode上所有数据块的列表
2 DataNode启动时向NameNode注册,通过后周期性地向NameNode上报blockReport,每3秒向NameNode发送一次心跳,NameNode返回对该DataNode的指令,如将数据块复制到另一台机器,或删除某个数据块等···而当某一个DataNode超过10min还没向NameNode发送心跳,此时NameNode就会判定该DataNode不可用,此时客户端的读写操作就不会再传达到该DataNode上
3 hadoop集群刚开始启动时会进入安全模式&#xff08;99.99%&#xff09;&#xff0c;就用到了心跳机制&#xff0c;其实就是在集群刚启动的时候&#xff0c;每一个DataNode都会向NameNode发送blockReport&#xff0c;NameNode会统计它们上报的总block数&#xff0c;除以一开始知道的总个数total&#xff0c;当 block/total <99.99% 时&#xff0c;会触发安全模式&#xff0c;安全模式下客户端就没法向HDFS写数据&#xff0c;只能进行读数据。