Hadoop HDFS原理分析
Hadoop
Hadoop由两部分组成,分别是分布式文件系统(HDFS)和分布式计算框架MapReduce
HDFS
架构图
NameNode
- 负责文件元数据信息的操作以及客户端的请求
- 管理HDFS文件系统的命名空间
- 维护文件树中所有的文件和文件夹的元数据信息以及文件到快的对应关系和块到节点的对应关系
- 单个NameNode支持4000台DataNode集群
- NameNode在内存中保存着整个文件系统的名字空间和文件数据块的地址映射
元数据
文件名,文件目录结构,文件创建时间,文件副本数,文件权限,每个文件的block列表
fsimage:元数据镜像文件,里面记录了自最后一次检查点之前HDFS文件系统中所有目录和文件的序列化信息;
注意:Block的位置信息不会保存到fsimage,Block保存在哪个DataNode由DataNode启动时上报。
edits:操作日志文件,保存了自最后一次检查点之后所有对HDFS文件的操作,比如:增加文件、重名名文件、删除文件
在NameNode启动的时候,先将fsimage中的文件系统元数据信息加载到内存,然后根据edits中的记录将内存中的元数据同步到最新状态;所以,这两个文件一旦损坏或丢失,将导致整个HDFS文件系统不可用。
为了保证元数据文件的高可用系统,一般的做法,将dfs.namenode.name.dir设置成一逗号分隔的多个目录,这个目录至少不要在一块磁盘上,最后在不同的机器上。
为了避免edits文件过大,SecondaryNameNode会按照时间阈值或者大小阈值,周期性的将fsimage和edits合并,然后将最新的fsimage推送给NameNode。
SecondaryNameNode
- 镜像备份
- 日志与镜像的定期合并
DataNode
- 处理文件内容的读写请求
- 一个数据快在DataNode以文件存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据,包括数据块的长度,块数据的校验和,以及时间戳
- DataNode启动后,周期性的向NameNode上报所有的块信息。
- 每3秒保持一次心跳,如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
- 文件划分成块,默认大小128M,以快为单位,每个快有多个副本(默认3个)存储不同的机器上。
Block
Block数据块是HDFS文件系统基本的存储单位
小于一个快的文件,不会占据整个快的空间
使用Block有利于容灾备份
Hadoop1.x 默认64M,Hadoop2.X默认128M
Block数据块大小设置较大的原因:
- 减少文件寻址时间
- 减少管理快的数据开销,因每个快都需要在NameNode上有对应的记录
- 对数据快进行读写,减少建立网络的连接成本
优点
- 一个文件的大小可以大于网络中任意一个磁盘的容量
- 使用抽象块而非整个文件作为存储单元,大大简化了存储子系统的设计
- 块还非常适合用于数据备份进而提供数据容错能力和提高可用性
合并流程
- 将hdfs更新记录写入一个新的文件--edits.new
- 将fsimage和edits文件通过http协议发送至secondary namenode
- 将fsimage和edits合并,生成一个新的文件fsimage.ckpt。
- 将生成的fsimage.ckpt文件通过http协议发送至NameNode
- 重命名fsimage.ckpt为fsimage,edits.new为edits
读流程
- 客户端通过调用FileSystem对象中的open()方法来读取需要的数据
- DistributedFileSystem会通过RPC协议调用NameNode来确定请求文件块所在的位置
NameNode只会返回所调用文件中开始的几个块而不是全部返回。对于每个返回的块,都包含块所在的DataNode的地址。随后,这些返回的DataNode会按照Hadoop集群的拓扑结构得出客户端的距离,然后在进行排序。如果客户端本身就是DataNode,那么它就从本地读取文件。其次,DistributedFileSystem会向客户端返回一个支持定位的输入流对象FSDataInputStream,用于给客户端读取数据。FSDataInputStream包含一个DFSInputStream对象,这个对象来管理DataNode和NameNode之间的IO
- 当以上步骤完成时,客户端便会在这个输入流上调用read()方法
- DFSInputStream对象中包含文件开始部分数据块所在的DataNode地址,首先它会连接文件第一个块最近的DataNode,随后在数据流中重复调用read方法,直到这个块读完为止。
- 当第一个块读取完毕时,DFSInputStream会关闭连接,并查找存储下一个数据块距离客户端最近的DataNode,以上这些步骤对客户端来说都是透明的。
- 客户端按照DFSInputStream打开和DataNode连接返回的数据流的顺序读取该块,它也会调用NameNode来检查下一组块所在的DataNode位置信息。当完成所有块的读取时,客户端则会在DFSInputStream中调用close()方法。
写流程
- 客户端发送请求,调用DistributedFileSystem API的create方法去请求namenode,并告诉namenode上传文件的文件名、文件大小、文件拥有者。
- namenode根据以上信息算出文件需要切成多少块block,以及block要存放在哪个datanode上,并将这些信息返回给客户端。
- 客户端调用FSDataInputStream API的write方法首先将其中一个block写在datanode上,每一个block默认都有3个副本,并不是由客户端分别往3个datanode上写3份,而是由
已经上传了block的datanode产生新的线程,由这个namenode按照放置副本规则往其它datanode写副本,这样的优势就是快。
- 写完后返回给客户端一个信息,然后客户端在将信息反馈给namenode。