HDFS是一个分布式文件存储系统,是 hadoop 生态中用于解决分布式存储问题的核心,是由谷歌的dfs论文催生出的分布式文件成存储解决方案。
存储模型
- 文件线性按字节切割成(
block
),具有offset
,id
- 文件与文件的
block
大小可以不一样 - 一个文件除最后一个
block
,其他block
大小一致 block
的大小依据硬盘的 I/O 特性调整block
被分散存放在集群的节点中,具有location
block
具有副本(replication
),没有主从概念,副本不能出现在同一个节点- 副本是可以满足可靠性和性能的关键
- 文件上传可以指定
block
大小和副本数,上传后只能修改副本数 - 一次写入多次读取,不支持修改
- 支持追加数据
HDFS
中的文件在物理上是分块存储(block
),块的大小可以通过配置参数(dfs.blocksize
)来规定,默认大小在hadoop2.x
版本中是128M,老版本中是64M- 文件的各个
block
的存储管理由DN
节点承担,DN
是HDFS
集群从节点,每一个block
都可以在多个DN
上存储多个副本(副本数量也可以通过参数设置(dfs.replication
),同一个block
不会存储多份(大于1)在同一个DN
上,因为这样没有意义 - 文件的各个块存储在
DN
的磁盘目录中(hdfs-site.xml
文件中配置的(dfs.datanode.data.dir
)节点的值+/current/BP-190247797-192.168.10.220-1460040893538/current/finalized
)
finalized
,rbw
这两个目录都是用于实际存储 HDFS
中 block
的数据,里面包含许多block_xx
文件以及相应的.meta
文件,.meta
文件包含了checksum
信息。
rbw
是“replica being written”的意思,该目录用于存储用户当前正在写入的数据。
数据块文件是以 blk
开头的
一个是数据本身,一个是数据的元信息(.meta
)。
注:设计数据块冗余度原则:一般跟数据节点一样;但是最大不要超过3,在 Hadoop 3.x 以前,会造成存储空间的极大浪费,Hadoop 3.x 以后,HDFS 纠缠码(Erasure Coding)技术,大大的节约存储的空间
架构设计
-
HDFS
是一个主从(Master/Slaves)架构 - 由一个
NN
和一些DN
组成 - 面向文件包含:文件数据(data)和文件元数据(metadata)
-
NN
负责存储和管理元数据,并维护了一个层次性的文件目录树 -
DN
负责存储文件数据(block块),并提供 block 的读写 -
DN
与NN
维持心跳,并汇报自己持有的block
信息(每三秒一次心跳信息带有NN
给DN
的命令,周期性(一小时)的向NN
汇报所有block
块信息)
HDFS
是设计成适应一次写入,多次读出的场景,且不支持文件的修改
(注:适合用来做数据分析,并不适合用来做网盘应用,因为,不便修改,延迟大,网络开销大,成本太高)
角色功能
NameNode(名称节点/主节点)
- 完全基于内存存储文件元数据、目录结构、文件 block 的映射
- 需要持久化方案保证数据可靠性
- 提供副本放置策略
- 管理文件系统的命名空间
- 维护文件目录树及整个树内所有文件和目录
- 记录各个块所在的数据节点信息(并不永久保存块的位置信息,因为这些信息会在系统启动时根据数据节点信息重建)
- 管理数据的元信息(数据文件的
dfs.block.size+10%
,block locations
,dfs.replication
,block count
)
日志文件(edits 文件)
记录 client
执行创建,移动,修改文件的信息,同时体现了 HDFS
的最新的状态(二进制文件),它分布在磁盘上的多个文件,名称由前缀 edits
及后缀组成.后缀值是该文件包含的事务 ID,同一时刻只有一个文件处于可读写状态.为避免数据丢失,事务完成后 client
端在执行成功前,文件会进行更新和同步,当 NN
向多个目录写数据时,只有在所有操作更新并同步到每个副本之后执行才成功。
edits_inprogress_0000000000000000107
代表:正在操作的日志文件
元信息文件(fsimage 文件)
记录的是数据块的位置信息、数据块的冗余信息(二进制文件)
由于 edits
文件记录了最新状态信息,并且随着操作越多,edits
文件就会越大,把 edits
文件中最新的信息写到 fsimage
文件中就解决了 edits
文件数量多不方便管理的情况。
没有体现 HDFS
的最新状态。
每个 fsimage
文件都是文件系统元数据的一个完整的永久性的检查点。
可能产生的问题
- 由于只有在重启时
fsimage
和edits
才会进行合并,得到一个新的fsimage
文件,但是在实际生产环境中很少会重启集群. NN
的重启需要花费很长时间,因为会有很多改动需要合并到 fsimage 文件上- 如果
NN
挂掉,fsimage
文件没有更新内容,从而丢失很多改动。
为了解决上述问题: 就引入了 Secondary NameNode
DataNode(数据节点)
负责管理和存储文件块 block
- 基于本地磁盘存储
block
(文件的形式) - 此处的元数据包括数据块的长度、块数据的校验和、时间戳
- 并保存
block
的效验和数据保证block
的可靠性 - 与
NN
保持心跳,汇报block
列表状态
SecondaryNameNode(检查点节点)
职责:日志信息的合并
在非 Ha
模式下,SNN
一般是独立的节点,周期完成对 NN
的 EditLog
向 FsImage
合并,减少 EditLog
大小,减少 NN
启动时间。
- 为什么引入SecondaryNameNode
- 为什么元数据存储在NameNode在内存中?
- 这样做有什么问题?如何解决?
- HDFS 编辑日志文件 editlog:在NameNode节点中的编辑日志 editlog 中,记录下来客户端对 HDFS 的所有更改的记录;每次更改对应一个事务,每个事务有一个事务编号;事务编号递增
- 作用:一旦系统出故障,可以根据 editlog 恢复元数据;
- 但 editlog 日志大小会随着时间变的越来越大,导致系统重启,根据日志恢复元数据的时间会越来越长;
- 为了避免这种情况,引入检查点机制checkpoint,命名空间镜像 fsimage 就是 HDFS 元数据的持久性检查点,即将内存中的元数据落磁盘生成的文件;
- 此时,
NN
如果重启,可以将磁盘中的 fsimage 文件读入内容,将元数据恢复到某一个检查点,然后再执行检查点之后记录的编辑日志 editlog ,最后完全恢复元数据。 - 随着时间的推移,editlog 记录的日志会变多,那么当 NN 重启,恢复元数据过程中,会花越来越长的时间执行 editlog 中的每一个日志;而在 NN 元数据恢复期间,HDFS不可用。
- 为了解决此问题,引入 SNN 辅助 NN,用来合并 fsimage 及 editlog
Checkpoint 机制
- SNN 首先请求原 NN 进行 edits 的滚动,这样新的编辑操作就能够进入新的文件中
- SNN 通过 HTTP GET 方式读取原
NN
中的fsimage
及edits
进行备份 - SNN 读取
fsimage
到内存中,然后执行edits
中的每个操作,并创建一个新的统一的fsimage文件,有 ckpt 后缀 - SNN 通过 HTTP PUT 方式将新的 fsimage 发送到原 NN
- 原 NN 用新的 fsimage 替换旧的 fsimage,同时系统会更新 fsimage 文件到记录检查点的时间。
- 这个过程结束后,NN 就有了最新的fsimage文件和更小的edits文件
创建检查点 checkpoint
的两大条件:
hadoop 1.x版本:
1、每隔 3600 秒(fs.checkpoint.period
参数设置最大时间间隔)
2、edits
文件达到了 64 M(fs.checkpoint.size
参数设置),即使此时没有达到period的时限也会执行 checkpoint
hadoop 2.x版本:
1、每隔 3600 秒(dfs.namenode.checkpoint.period
参数设置最大时间间隔)
2、另外,SNN
每1分钟检查一次,从上一检查点开始,edits日志文件中是否已包括100万个事务,如果是,也会创建检查点
2.x版本checkpoint相关属性(hdfs-site.xml)
属性 | 值 | 解释 |
dfs.namenode.checkpoint.period | 3600秒(即1小时) | The number of seconds between two periodic checkpoints. |
dfs.namenode.checkpoint.txns | 1000000 | The Secondary NameNode or CheckpointNode will create a checkpoint of the namespace every ‘dfs.namenode.checkpoint.txns’ transactions, regardless of whether ‘dfs.namenode.checkpoint.period’ has expired. |
dfs.namenode.checkpoint.check.period | 60(1分钟) | The SecondaryNameNode and CheckpointNode will poll the NameNode every ‘dfs.namenode.checkpoint.check.period’ seconds to query the number of uncheckpointed transactions. |
元数据持久化
- 任何对文件系统元数据产生修改的操作,
NN
都会使用一种称为EditLog
的事务日志记录下来 - 使用
FsImage
存储内存所有的元数据状态 - 使用本地磁盘保存
EditLog
和FsImage
-
Editlog
具有完整性,数据丢失少,但恢复速度慢,并有体积膨胀风险 -
FsImage
具有恢复速度快,体积与内存数据相当,但不能实时保存,数据丢失多 NN
使用了FsImage
+EditLog
整合的方案;
- 滚动将增量的
EditLog
更新到FsImage
,以保证更近时点的FsImage
和更小的EditLog
体积
安全模式(safe mode)
- NN 启动后会进入一个称为安全模式的特殊状态,是 HDFS 的一种自我保护,用于检查数据块的副本率
- 当集群启动的时候,会首先进入安全模式。当系统处于安全模式时会检查数据块的完整性和一致性。
NN
从所有DN
接受心跳信号和块状态报告- 每当
NN
检测确认某个数据块副本数目达到这个最小值,那么该数据块就会被认为是副本安全(safely replicated
)的 - 在一定百分比(这个参数可配置)的数据块被
NN
检测确定是安全之后(加上一个额外的30秒等待时间),NN
将退出安全模式状态。 - 接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他
DN
上。 HDFS
搭建时会格式化,格式化操作会产生一个空的FsImage
- 当
NameNode
启动时,它从硬盘中读取EditLog
和FsImage
- 将所有
EditLog
中的事务作用在内存中的FsImage
上 - 并将这个新版本的
FsImage
从内存中保存到本地磁盘上 - 然后删除旧的
EditLog
,因为旧的EditLog
的事务都已经作用在FsImage
上了
block 副本放置策略
2.x
- 第一个副本:放置在上传文件的
DataNode
;如果时集群外提交,则随机挑选一台磁盘不太满,CPU 不太忙的节点。 - 第二个副本:放置在于第一个副本不同的机架的节点上。
- 第三个副本:与第二个副本相同机架的节点。
- 更多副本:随机节点
机架感知
将数据块保存在两个不同的机架中保证数据的安全性,但是这可能带来存取效率的问题,那么就在同一个机架中进行再次备份,这样既保证数据的安全性又解决存取效率问题。
在 1.x
版本副本放置策略
可能产生的问题是前两个副本在同一机架当机架出现问题时会丢失两个副本
在 2.x
版本副本放置策略
读写流程
写流程(write)
-
Client
和NN
创建文件元数据信息 -
NN
判定元数据是否有效 -
NN
触发副本放置策略,返回元数据信息 -
Client
和DN
建立Pipeline
连接 -
Client
将块切分成packet
(64KB),并使用chunk
(512B)+chucksum
(4B)填充 -
Client
将packet
放入发送队列dataqueue
中,并向第一个DN
发送 - 第一个
DN
收到packet
后本地保存并发送给第二个DN
- 第二个
DN
收到packet
后本地保存并发送给第三个DN
- 这一个过程中,上游节点同时发送下一个
packet
- 生活中类比工厂的流水线:结论:流式其实也是变种的并行计算
-
HDFS
使用这种传输方式,副本数对于Client
是透明的 - 当
block
传输完成,DN
们各自向NN
汇报,同时Client
继续传输下一个block
- 所以,
Client
的传输和block
的汇报也是并行的
重点:NN 触发副本放置策略,Pipline 流式传输是变种的并行
读流程(read)
- 为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本。
- 如果在读取程序的同一个机架上有一个副本,那么就读取该副本。
- 如果一个 HDFS 集群跨越多个数据中心,那么客户端也就首先读取本地数据中心的副本。
- 语义:下载一个文件:
-
Client
和NN
交互文件元数据获取fileBlockInfo
-
NN
会按照距离策略排序返回 -
Client
尝试下载block
并效验数据完整性
- 语义:下载一个文件其实是获取文件的所有
block
元数据,那么子集获取某些block
就应该成立。
HDFS
支持Client
给出文件的offset
自定义链接哪些block
的DN
,自定义获取数据- 这个是支持计算层的分治、并行计算的核心。
HDFS 的底层原理
HDFS 的底层通信原理采用的是:RPC和动态代理对象 Proxy
RPC(Remote Procedure Call)
远程过程调用。也就是说,调用过程代码并不是在调用者本地运行,而是要实现调用者与被调用者两地之间的连接与通信。
RPC
的基本通信模型时是基于 Client
/Server
进程之间相互通信模型的一种同步通信形式;它对 Client
提供了远程服务的过程抽象,其底层消息传递操作对 Client
是透明的。
在 RPC
中,Client
即是请求服务的调用者(Caller
),而 Server
则是执行 Client
的请求而被调用的程序(Callee
)。
动态代理对象
为其他对象提供一种代理以控制对这个对象的访问。
核心是使用JDK
的Proxy
类
本文部分引用:
《Hadoop权威指南(第四版)》
马士兵教育 https://www.bilibili.com/video/BV1vJ411L7Rf