首页 > 技术文章 > Hadoop之HDFS理解

hhachi 2020-04-19 19:08 原文

理论知识点

存储模型

  • 文件线性按字节切割成block,具有offset,id(所有的文件都可以看作字节数组)
  • 文件与文件的block大小可以不一样
  • 一个文件除了最后一个block,其他的block大小一致
  • block的大小根据硬件的I/O特性调整(1.X默认是64M,2.X默认是128M)
  • block被分散存放到集群的节点中,具有location
  • block具有副本,没有主从概念,副本不能出现在同一节点(满足可靠性)
  • 文件上传可以指定block大小和副本数量,上传后只能修改副本数量
  • 不支持修改,支持追加数据

架构设计

  • HDFS是一个主从架构
  • 由一个NameNode和一些DateNode组成
  • 面向文件包含:文件数据(data)和文件元数据(metadata)
  • NameNode负责存储和管理元数据,并维护了一个层次型的文件目录树(不是物理的磁盘)
  • DataNode负责存储文件数据,其中文件数据包括原始数据和数据的校验和。
  • DateNode和NameNode维持心跳(默认是3秒),并汇报持有的block信息
  • clint和NameNode交互文件元数据和DateNode交互文件的block数据

角色功能

角色即jvm进程

NameNode

  • 完全基于内存存储文件元数据、目录结构、文件block映射
  • 需要提供持久化方案保证数据的可靠性
    所有基于内存存储的,都需要提供持久化方案。分两种,其一就是记录变换,记录每一步都干了什么,在HDFS中称之为Editlog,完整性比较好,加载恢复数据慢且占空间;第二种则是快照,也就是把当前时间点的内存数据记录下来,HDFS称之为FsImage,恢复速度快但是因为是间隔的,容易丢失数据。
  • 提供副本放置策咯

DataNode

  • 基于本地磁盘存储block(文件的形式)
  • 保存block的校验和数据保证block可靠性
  • 与NameNode保持心跳,汇报block列表状态

元数据持久化

在持久化的时候,文件属性会持久化,但是文件的每一个block不会持久化。因为如果启动的时候有的DateNode节点可能宕机,造成clint读取失败。

  • 任何堆文件系统元数据产生修改的操作,NameNode都会使用一种称为EditLog的事务日志记录下来
  • 使用FsImage存储内存所有的元数据状态
  • 使用本地磁盘保存EditLog和FsImage
  • EditLog具有完整性,数据丢失少,但是恢复速度慢、并有体积膨胀的风险
  • FsImage具有恢复速度快,体积与内存数据相当,但不能实时保存,数据丢失多
  • NameNode使用了FsImage+EditLog整合的方案
    例如:系统当前磁盘里记录了8点钟的快照(FsImage)也记录了8-9点钟的各种操作(Editlog),那么HDFS想要在9点钟持久化,只需要在8点钟的FsImage上进行增量操作(Editlog),写回磁盘里。

安全模式

  • HDFS搭建时会格式化,格式化操作会产生一个空的FsImage
  • 当NmaeNode启动时,它从硬盘钟读取EditLog和FsImage
  • 将所有EditLog中的事务作用在内存中的FsImage上
  • 并将这个新版本的FsImage从内存中保存到本地磁盘上
  • 然后删除旧的EditLog,因为这个旧的EditLog的事务都已经作用在FsImage上了
  • NmaeNode启动后会进入安全模式,在安全模式中,NameNode不会进行数据块的复制。
  • NameNode从所有的DateNode接收心跳信号和块状态报告,每当NameNode检测确认某个数据块的副本数达到最小值,那么该数据块会被认为是安全的。
  • 在一定百分比的数据块被NameNode检测确认安全后,加上额外的30秒,NameNode将退出安全模式
  • 剩下没确定的数据块的副本没有达到指定数目,会复制到其他DateNode上

副本放置策略

  • 第一个副本:放置在上传文件的DataNode;如果是集群外提交,则随机挑选一台磁盘不太满,cpu不太忙的节点
  • 第二个副本:放置在与第一个副本不同的机架节点上
  • 第三个副本:放置在与第二个副本相同的机架节点上
  • 更多的副本:随机节点

读写流程

写流程

  • 1.Client与NameNode进行通信,NameNode创建文件的元数据,并判断元数据是否有效(文件是否存在,权限),写入EditLog。
  • 2.Client会对文件分割成块(block),然后Client向NameNode请求上传第一个块
  • 2.NameNode会向Client提供副本放置策略,返回一个有序的DateNode列表
  • 3.Client与最近的DateNode进行连接(pipeline),并将块切分成packet(64KB),使用chunk(512B)+ checksum(4B)填充,每当满64KB,就会向第一个DateNode发送
  • 4.第一个DateNode收到packet后,本地保存并发送到第二个DateNode,第二个DateNode收到packet后本地保存并发送给第三个DateNode,每个DateNode收到packet都会检验数据,并向前一个DateNode发送ack。这一过程中,Client会同时给第一个DateNode发送packet。
  • 5.当整个块传输完成后,DateNode会向NameNode汇报。同时Client传输下一个块。

读流程

  • 1.Client向NameNode请求下载文件,即向NameNode通信查询元数据(block所在的DateNode节点),找到文件块所在的DateNode服务器
  • 2.Client尝试下载block,并校验数据完整性

HDFS中的高可用

单点故障

  • 高可用方案:HA(High Availiable)
  • 多个NameNode,主备切换

压力过大,内存受限

  • 联邦机制:Federation(元数据分片)
  • 多个NameNode,管理不同的元数据

HA

要实现hadoop的高可用,需要满足:

  • 主备切换对于用户来说是透明的
  • 保证主备的数据一致
  • 解决脑裂问题

NameNode主备切换

Hadoop借助Zookeeper来实现,首先active状态和standby状态的NameNode都会有各自的zkfc,zkfc是一个在NameNode中的进程,时时监控这NameNode的状态。

  • 1.在NameNode启动时,两个zkfc会创建目录 也就是抢锁的过程,谁创建目录成功谁是active
  • 2.如果active挂掉了,zkfc会知道,会把锁删除,触发standby的callback函数,继而上锁。
  • 3.standby的zkfc会查看active的NameNode是否真正的挂掉了,如果真正挂了会切换状态为active

保证主备数据一致

运用QJM,基于Paxos算法(解决分布式环境中数据如何达成一致)

  • 1.主备NameNode中间会有2N+1个JN(JournalNode),每次NameNode写editlog只要其中N+1以及以上的JN返回成功,NameNode就认为写入成功
  • 2.standby状态的NameNode会定时从JN中读取一批editlog,并作用于内存中的Fsimage中,通知active,avtive的NameNode过来取。
  • 3.DaateNode会和active、standby保持心跳。

脑裂问题

Hadoop中脑裂问题有两方面

  • 第一种是共享存储中的脑裂问题,确保只有一个NameNode可以写成功
    NameNode每次写Editlog都需要传递一个编号Epoch给JN,JN会对比Epoch,如果比自己保存的Epoch大或相同,则可以写,JN更新自己的Epoch到最新,否则拒绝操作。在切换时,Standby转换为Active时,会把Epoch+1,这样就防止即使之前的NameNode向JN写日志,也会失败。
  • 第二种是DataNode的脑裂问题,确保只有一个NameNode能命令DataNode。
    每个NameNode改变状态的时候,向DataNode发送自己的状态和一个序列号。并维护这个序列号,当NameNode再次改变时,新的NameNode接收到DataNode心跳时会返回自己的active状态和一个更大的序列号,当DataNode接收这个序列号时,认为该NameNode是新的active。如果原来的active这时候恢复了,与DataNode链接时发送的还是原来的序列号,小于新的序列号,这时DataNode会拒绝NameNode的命令

联邦机制

  • 这些NameNode直接相互独立,各自分工管理自己的区域,且不需要互相协调,一个NameNode挂掉了不会影响其他的NameNode
  • DataNode被用作通用的数据存储设备,每个DataNode要向集群中所有的NameNode注册,且周期性的向所有NameNode发送心跳和报告,并执行来自所有NameNode的命令
  • 一个Block Pool由属于同一个NameSpace的数据块组成,每个DataNode可能会存储集群中所有Block Pool数据块,Block Pool之间不会交流,各自管理
  • NameNode和Block Pool一起被称作NameNode volume,它是管理的基本单位,当一个NameNode被删除后,所有DataNode上与其对应的Block Pool也会被删除。当集群升级时,每个NameNode volume作为一个基本单元进行升级

推荐阅读