1. 基本概述
1.HDFS本身是一个典型的MS结构的框架:主节点NameNode,从节点DataNode -通过slaves文件来指定从节点,HDFS的主节点通过hdfs-site.xml来指定
2.HDFS对会上传的数据进行切分,切成数据块(Block)来存储到不同的DataNode上
3.HDFS会对存储的数据来进行备份,每一个备份称之为是一个副本(replication/replicas)。副本策略默认为3
4.NameNode需要记录元数据(metadata),元数据就类似于账本
5.HDFS仿照Linux设计了一套文件系统,允许将文件存储在不同的虚拟路径下,根路径是/
2. block
1.Block是HDFS中数据存储的基本形式,即任意一个文件存储到HDFS上之后都是以Block形式来存储
2.在Hadoop2.0中,Block的大小默认是128M,可以通过dfs.blocksize来进行调节,单位是字节,放在hdfs-site.xml中
3.如果一个文件不足一个Block的默认大小,那么这个文件放到HDFS上之后,文件本身是多大就占多大的地方,对应的Block也是文件的实际大小
4.HDFS会为每一个Block分配一个唯一的Block ID
5.HDFS会为每一个Block分配一个递增的Generation Stamp
Block类,它含有三个成员:blockId,numBytes和generationStamp。numBytes即block的大小,而另外两个分别是什么呢?blockId是block的标识符,可以从block文件名中看到,例如${hadoop.tmp.dir}/dfs/data/current/blk_826540629399449945,这一串数字就是blockId。同目录下另一个meta文件,如blk_826540629399449945_1017.meta,1017即是generationStamp。从Block类本身可以看到,generationStamp在compare、equals等操作上起到一个对blockId的辅助作用,由此猜测多个blocks可能拥有相同的blockId,彼此的generationStamp不同。
6.HDFS切块的意义
- 能够存储超大文件
- 能够快速的备份
3. NameNode
1.NameNode是HDFS中的主节点,默认情况下HDFS中,NameNode只有一个
2.作用:记录元数据,管理DataNode
3.元数据是描述数据的数据,可以理解为是一个账本
4.HDFS中的元数据主要包含
- 文件的上传路径
- 上传用户
- 文件的权限
- 文件大小
- Block大小
- 文件和BlockID的映射关系
- Block的Genertion Stamp
- BlockID和DataNode的映射关系
- 副本数量
5.NameNode将元数据维系在内存以及磁盘中
- 维系在内存中的目的是为了读写快
- 维系在磁盘中的目的是为了持久化
6.元数据在磁盘上的存储路径由属性hadoop.tmp.dir来决定,这个属性是放在core-site.xml中。如果不指定,那么默认是放在/tmp下
7.和元数据相关的文件
- edits:操作文件。记录NameNode接收的每一个写请求
- fsimage:元映像文件。记录元数据的,但是注意,这个文件中的元数据和内存中的元数据并不是同步的
8.当NameNode收到写请求的时候,会先将写请求这个命令本身记录到edits_inprogress文件中,如果记录成功更新内存中的元数据。如果内存中的元数据更新成功,就会给客户端返回ack信号。注意,此时fsimage文件中的元数据并没有修改
9.当达到指定条件之后,edits_inprogress文件会产生滚动,滚动生成一个新的edits_inprogress,原来的edits_inprogress会自动重命名为edits文件,HDFS会将edits文件中记录的命令取出来一一执行更新到fsimage文件中,修改fsimage中的元数据
10.edits_inprogress文件的滚动条件
- 空间:当edits_inprogress文件达到指定大小(默认是64M,可以通过fs.checkpoint.size来调节,单位是字节,放在core-site.xml中)的时候,会产生滚动
- 时间:当距离上一次滚动的时间达到指定间隔(默认是60min,可以通过fs.checkpoint.period来调节,单位是s)的时候,edits_inprogress文件也会产生滚动。注意,Hadoop第一次启动的时候,启动1min之后edits_inprogress自动的滚动一次
- 重启:当NameNode重启的时候,edits_inprogress文件也会自动滚动
- 强制:利用hadoop dfsadmin -rollEdits来强制滚动
11.NameNode通过心跳机制来管理DataNode - DataNode定时向NameNode发送心跳信息
12.DataNode每隔3s(通过dfs.heartbeat.interval来调节,单位是s,放在hdfs-site.xml中)向NameNode发送一次心跳,当NameNode超过10min没有收到DataNode的心跳,那么就认为这个DataNode已经lost(丢失),这个时候NameNode会将这个DataNode上的数据备份到其他节点上来保证副本数量
13.心跳信息
a.DataNode的状态(预服役、服役、预退役)
b.clusterid:集群编号
- 在对NameNode进行格式化(hadoop namenode -format)的时候,会自动计算产生一个clusterid
- 每次格式化都会重新计算产生一个新的clusterid
- 当HDFS集群启动的时候,NameNode在接收第一次心跳的时候,会将clusterid在心跳响应中分发给每一个 DataNode
- 当DataNode收到clusterid之后就不会再次接收新的clusterid了
- DataNode每一次通信都会携带clusterid,当NameNode收到DataNode的信息的时候会先校验clusterid是否一致。如果不一致则直接拒绝;如果一致才会处理这个DataNode的请求
- 多次格式化,会导致NameNode或者DataNode启动不了
c.DataNode存储的Block信息
4. HDFS写数据流程
1、使用HDFS 提供的客户端 Client,向远程的 namenode 发起 RPC 请求
2、namenode 会检查要创建的文件是否已经存在,创建者是否有权限进行操作,成功则会为文件创建一个记录,否则会让客户端抛出异常;
3、当客户端开始写入文件的时候,客户端会将文件切分成多个 packets,并在内部以数据队列“data queue(数据队列)”的形式管理这些 packets,并向 namenode 申请 datanode列表,获取用来存储 replicas 的合适的 datanode 列表,列表的大小根据 namenode 中 replication的设定而定;
4、开始以 pipeline(管道)的形式将 packet 写入所有的 replicas 中。客户端把 packet 以流的方式写入第一个 datanode,该 datanode 把该 packet 存储之后,再将其传递给在此 pipeline中的下一个 datanode,直到最后一个 datanode,这种写数据的方式呈流水线的形式。
5、最后一个 datanode 成功存储之后会返回一个 ack packet(确认队列),在 pipeline 里传递至客户端,在客户端的开发库内部维护着”ack queue”,成功收到 datanode 返回的 ackpacket 后会从”data queue”移除相应的 packet。
6、如果传输过程中,有某个 datanode 出现了故障,那么当前的 pipeline 会被关闭,出现故障的 datanode 会从当前的 pipeline 中移除,剩余的 block 会继续剩下的 datanode 中继续以 pipeline 的形式传输,同时 namenode 会分配一个新的 datanode,保持 replicas 设定的数量。
7、客户端完成数据的写入后,会对数据流调用 close()方法,关闭数据流;
8、只要写入了 dfs.replication.min(最小写入成功的副本数)的副本数(默认为 1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标复本数(dfs.replication的默认值为 3),因为 namenode 已经知道文件由哪些块组成,所以它在返回成功前只需要等待数据块进行最小量的复制。
5. HDFS读数据流程
1、使用 HDFS 提供的客户端 Client,向远程的 namenode 发起 RPC 请求;
2、namenode 会视情况返回文件的全部 block 列表,对于每个 block,namenode 都会返回有该 block 拷贝的 datanode 地址;
3、客户端Client会选取离客户端最近的datanode来读取block;如果客户端本身就是datanode,那么将从本地直接获取数据;
4、读取完当前 block 的数据后,关闭当前的 datanode 链接,并为读取下一个 block 寻找最佳的 datanode;
5、当读完列表 block 后,且文件读取还没有结束,客户端会继续向 namenode 获取下一批的block 列表;
6、读取完一个 block 都会进行 checksum 验证,如果读取 datanode 时出现错误,客户端会通知 namenode,然后再从下一个拥有该 block 拷贝的 datanode 继续读。