-
HDFS集群分为两大角色:NameNode、DataNode (Secondary Namenode)
-
NameNode 负责管理整个文件系统的元数据
-
DataNode 负责管理用户的文件块(Block)
-
文件会按照固定的大小(默认 128 M)切成若干文件块(Block)后分布式存储在若干台 DataNode 服务器上
-
每一个文件块可以有多个副本,并存放在不同的 DataNode 服务器上
-
DataNode 会定期向 NameNode 汇报自身所保存的文件块(Block)信息,而 NameNode 则会负责保持文件的副本数量
HDFS 的内部工作机制对客户端保持透明,客户端通过向 NameNode 发送请求来访问 HDFS 存放的文件
HDFS 写数据流程
# 上传 file.data 到 HDFS (300M)
hadoop fs -put file.data /data/file.data
流程图
详细步骤
- 使用客户端命令行向 HDFS 上传 file.data(300M)文件时,客户端会向集群中的 NameNode 通信请求上传文件到 HDFS 目录树的 /data/file.data 路径
- NameNode 收到客户端请求后会检查 /data 目录 和 /data/file.data 文件是否存在,如果不存在则向客户端返回可以进行上传文件
- 由于上传的 file.data 文件是 300M(默认文件数据块 128M),文件需要被切分成三个文件数据库(Block)依次进行上传。客户端先向 NameNode 请求上传第一个文件数据块(Block)以及告诉 NameNode 存放文件数据块的副本数量(默认 3 个),NameNode 则会返回 3 个可以上传的 DataNode(假设是 DataNode1、DataNode2、DataNode4)
- 客户端选择与 DataNode1 建立网络连接,而 DataNode1 接着与 DataNode2 建立网络连接,DataNode2 与DataNode4 建立网络连接,(本质就是 RPC 远程调用形成 pipeline 网络连接链路通路)。通道建立完毕后,DataNode1 会向客户端作出应答,告诉客户端可以上传第一个文件数据块(Block)
- 客户端收到 DataNode1 的应答后,以 packet 包为单位向 DataNode1 上传数据流,上传的数据流是先存放到 DataNode1 的一个缓冲区,再缓冲区同时向其他 DataNode 缓冲区传,缓冲区再写到磁盘
- 上传过程中,DataNode 会进行校验,校验失败则传输失败,但是只要有一个 DataNode 传输成功即可。因为 NameNode 会判断成功的数量与备份要求的是否一致,不一致的话则会对数据进行同步使得一致。
- 第一个文件数据块(Block)上传完成后,会再重新与 NameNode 建立网络连接,请求上传第二个文件数据块(Block),即重复第3步流程,直到所有文件数据快(Block)全部上传为止。
副本放置策略 - 就近原则
挑选 DataNode 主要考虑空间与距离因素
- 第一个副本考虑选择与客户端距离最近的 DataNode(同机架)
- 第二个副本考虑跨机架选择一个 DataNode(增加副本的可靠性)
- 第三个副本挑选与第一个 DataNode 同机架的 DataNode(配置机架感知)
为了最小化全局带宽消耗和读取延迟,HDFS 试图满足来自离客户端最近的副本的读取请求。如果在与客户端相同的机架上存在一个副本,则首选该副本来满足读请求。如果 HDFS 集群跨越多个数据中心,那么驻留在本地数据中心的副本优于任何远程副本。
NameNode 安全模式(SafeMode)
- 在启动 NameNode 时,NameNode 进入一个称为 安全模式(SafeMode)的特殊状态。当 NameNode 处于该状态,不会发生文件数据块的复制。
- NameNode 从数据节点接收心跳和 BlockReport 消息。Blockreport包含DataNode托管的数据块列表。每个块都有一个指定的最小副本数量。当该数据块的最小副本数量(dfs.namenode.replication.min =1)已与
NameNode 签入时,就认为该块是安全复制的。当安全复制的数据块的可配置百分比(
dfs.namenode.safemode.threshold-pct =0.999f)通过NameNode(加上额外的30秒)检查后,
NameNode将退出安全模式状态。然后,它确定仍然少于指定副本数量的数据块列表(如果有的话)。然后
NameNode将这些块复制到其他数据节点。
安全模式(Safemode )开启时不能有写操作
安全模式(SafeMode)命令:
客户端写数据 DataNode 故障处理
- 客户端在写入文件数据块到 DataNode 发生故障时,客户端会关闭与 DataNode 之间的网络通信 pipeline 管线,以确保故障节点 pipeline 管线下游的 DataNode 不会漏掉 packet 数据包
- 客户端将故障 DataNode 通知给 NameNode,以便故障的 DataNode 在恢复后可以删除存储的部分数据块
- 客户端基于另外两个正常的 DataNode 构建一条新的 pipeline 管线,将剩下的文件数据块写入 pipeline 管线中正常的 DataNode
- 当 NameNode 注意到文件数据块副本数量不足的时候,就会在另一个 DataNode 节点上创建一个新的副本,后续的数据块则继续正常处理
HDFS 读数据流程
# 从 HDFS 下载 /data/file.data(300M) 文件到本地系统
hadoop fs -get /data/file.data ./
详细步骤
- 使用客户端命令行从 HDFS 下载 /data/file.data(300M)文件时,客户端与 NameNode 建立网络连接,并请求要下载的文件;NameNode 查看元数据,找到下载文件被切割的 3 个文件数据块(Block)所存放的 DataNode 信息,并返回给客户端
- 客户端首先下载第一个文件数据块(Block),而第一个文件数据块(Block)在 3 个 DataNode 上都有其副本,客户端根据就近原则去挑选一个 DataNode 建立网络连接,并请求读取文件数据块(Block);DataNode 读取磁盘数据流,向客户端发送数据流
- 客户端接收 DataNode 发送的数据流并暂存缓存中,然后写入到本地文件中,接着客户端重复步骤1、2 来下载第二个文件数据块(Block)(和第三个文件数据块(Block),然后数据以追加的形式写入到文件中,最终合并成一个完成的文件
客户端读数据 DataNode 故障处理
- 客户端在 DataNode 上读取文件数据块(Block)时发生故障(即客户端 与 DataNode 无法正常通信),客户端会试图从故障 DataNode 最邻近的一个保存副本的 DataNode 读取文件数据块(Block)
- 客户端会记录故障的 DataNode,以确保以后不会反复读取该故障 DataNode 节点上后续的文件数据块(Block)
- 客户端会通过校验和确认从 DataNode 读取的数据是否完整,如果发现有损坏的文件数据块(Block),客户端会试图从其他保存有副本的 DataNode 读取数据,并将被损坏的块通知给 NameNode
- NameNode 记录有损坏的文件数据块的 DataNode,通知其删除数据,并创建新的文件数据块的副本,以确保副本数量的一致
NameNode 工作机制
NameNode 职责
- 负责客户端请求的响应
- 管理元数据(即 HDFS 的目录结构及每一个文件的文件数据块(Block)信息(如 block id、block 副本数量,block 的存放位置等)
- 容错处理
NameNode 管理元数据
元数据的存储
-
内存元数据
NameNode 在内存(meta data)中保存着一份实时、完整的元数据
-
磁盘元数据镜像文件
NameNode 在磁盘中保存着某个时间点元数据在内存中的镜像 fsimage 文件(类似 redis 的 RDB)
- fsimage 包含 Hadoop 文件系统中的所有目录和文件 idnode 的序列化信息(具体而言,文件包含的信息有修改时间、访问时间、块大小和组成一个文件块信息等,目录包含的信息有修改时间、访问控制权限等)
- fsimage 并不包含 DataNode 的信息,而是包含 DataNode 上文件数据块(Block)的映射信息。
- 当一个新的 DataNode 加入到集群中,DataNode 会向 NameNode 提供文件数据块(Block)的信息,而 NameNode 会定期向 DataNode 的索取文件数据块的信息,以使得 NameNode 拥有最新的文件数据块(Block)映射信息
- 由于 fsimage 包含 Hadoop 文件系统中的所有目录和文件 idnode 的序列化信息,所以如果 fsimage 文件丢失或损坏,那么即使 DataNode 上有文件数据块(Block),也无法从 DataNode 上的获取数据
-
数据操作日志文件
NameNode 会把引起元数据变化的客户端操作记录在 edits 日志文件中(类似 redis 的 AOF)
元数据目录
-
在第一次部署好 Hadoop 集群的时候,需要在 NameNode 节点上格式化磁盘
[root@node-01 etc]# hadoop namenode -format
-
格式化完成之后,将会在 NameNode 存储路径的 current 目录下创建元数据目录
current/ |-- VERSION |-- edits_* |-- fsimage_* |-- fsimage_*.md5 |-- seen_txid
-
VERSION
namespaceID=1064465530 clusterID=CID-a1afedaf-49ed-4219-bae3-7b7268fca624 cTime=1616685687133 storageType=NAME_NODE blockpoolID=BP-593175008-192.168.229.21-1616685687133 layoutVersion=-65
- namespaceID:是文件系统的唯一标识符,在文件系统首次格式化之后生成的
- clusterID:是系统生成或手动指定的集群 ID
- cTime:NameNode 存储时间的创建时间
- storageType:说明这个文件存储的是什么进程的数据结构信息
- layoutVersion:HDFS 永久性数据结构的版本信息
- blockpoolID:针对每一个 Namespace 所对应的 blockpool 的 ID(DataNode Block 目录名)
-
seen_txid
该文件中记录的是 edits 日志滚动的序号,每次重启NameNode时,NameNode就知道要将会执行edits_0000001~ edits_seen_txid 的 edits 日志文件,已更新内存元数据
-
edits_* 查看器(OEV)
oev 是 offline edits viewer(离线 edits 查看器)的缩写,该工具可以使用 oev 工具查看 edits 日志文件和 fsimage 镜像文件内容
命令格式:hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径
- -p –processor
指定输出处理器: binary (二进制格式), xml (默认,XML格式),stats - -i –inputFile
输入edits文件(如果是xml后缀,表示XML格式,其他表示二进制) - -o –outputFile
输出文件,如果存在,则会覆盖
[root@node-01 current]# hdfs oev -i edits_inprogress_0000000000000000081 -o edits.xml
- -p –processor
-
images_* 查看器(OIV)
oiv 是 offline image viewer 的缩写,用于将 fsimage 文件的内容转储到指定文件里以便于阅读。OIV 在处理很大的 fsimage 文件时是相当快。
命令格式:hdfs oiv -p 文件类型 -i 编辑日志 -o 转换后文件输出路径
- -p –processor
指定输出处理器: LS、XML、FileDistribution - -i –inputFile
输入 fsimage 文件(如果是xml后缀,表示XML格式,其他表示二进制) - -o –outputFile
输出文件(如果存在,则会覆盖)
[root@node-01 current]# hdfs oiv -i fsimage_0000000000000000080 -p XML -o fsimage.xml
- -p –processor
-
元数据的内存加载
- NameNode 启动时,会将磁盘中 fsimage 文件中的内容加载到内存中,之后再执行数据操作日志 edits 文件,使得内存获得一份最新最完整的元数据
- NameNode 启动后,客户端的所有更新数据操作会同步写入到 edits 日志中(fsimage 文件一般很大,直接写入会导致系统运行缓慢)
元数据的 CheckPoint(检查点)机制
-
Secondary NameNode 会定期从 NameNode 上下载 fsimage 镜像和新生成的 edits 日志,然后加载 fsimage 镜像到内存中,接着顺序解析 edits 文件,对内存中的元数据对象进行修改整合
-
整合完成后,将内存元数据序列化成一个新的 fsimage 镜像文件,并将这个 fsimage 镜像文件上传给 NameNode
-
NameNode 接收到 Secondary NameNode 发送的新 fsimage 镜像文件后,即可将旧的 fsimage 镜像文件和 edist 日志替换掉
注1:Secondary NameNode 每次做 CheckPoint 操作时,第一次 CheckPoint 需要下载 fsimage 镜像文件,以后就不用下载,因为之前已经下载过了
注2:Namenode 和 Secondary NameNode 的工作目录存储结构完全相同,当 NameNode 故障退出需要重新恢复时,可以从 Secondary NameNode 的工作目录中将 fsimage 拷贝到 NameNode 的工作目录,以恢复NameNode 的元数据
CheckPoint(检查点)操作触发条件相关配置
CheckPoint(检查点)相关配置 hdfs-size.xml 如下:
-
Secondary NameNode 检查触发条件是否满足的频率配置(默认 60 s)
<property> <name>dfs.namenode.checkpoint.check.period</name> <value>60</value> </property>
-
Secondary NameNode 进程启动位置的配置
<property> <name>dfs.namenode.secondary.http-address</name> <value>node-02:9868</value></property>
-
Secondary NameNode 保存元数据文件的目录配置
<property> <name>dfs.namenode.checkpoint.dir</name> <value> /root/apps/hadoop-3.2.1/data/namesecondary</value> </property>
-
Secondary NameNode 最多重试次数(默认3次)
<property> <name>dfs.namenode.checkpoint.max-retries</name> <value>3</value></property>
-
Secondary NameNode 两次 CheckPoint 之间间隔时长(默认 1 小时)
<property> <name>dfs.namenode.checkpoint.period</name> <value>3600</value></property>
DataNode 工作机制
- DataNode 存储管理用户的文件数据块(Block),一个文件数据块(Block)在 DataNode 上以文件形式存储在磁盘上
- DataNode 定期向 NameNode 汇报自身所持有的所有文件数据块(Block)信息(汇报间隔时间由配置项 dfs.blockreport.intervalMsec 决定)
- 心跳检测默认每 3 秒检测一次(检测间隔时间由配置项 dfs.heartbeat.interval 决定),心跳检测返回结果带有 NameNode 给 DataNode 的命令(如复制文件数据块副本到另一个 DataNode 或删除某个损坏的文件数据块)
- 如果超过限定时间没有收到某个 DataNode 的心跳,NameNode 则认为该 DataNode 不可用
- DataNode 掉线后会触发副本检查机制,NameNode 会在集群内通知正常的 DataNode 进行副本复制
- Hadoop 集群运行中可以安全加入和退出一些 DataNode 节点
DataNode 职责
-
存储管理用户的文件数据块(Block)
-
数据:用户的文件数据块(Block)
-
元数据:文件数据块(Block)长度、文件数据块校验和、时间戳等
-
-
定期向 NameNode 汇报自身所持有的文件数据块(Block)信息(通过心跳信息上报)
<!-- 设置 datanode 向 namenode 报告 block 信息的时间间隔(默认6小时)<property> <name>dfs.blockreport.intervalMsec</name> <value>21600000</value> <description>Determines block reporting interval in milliseconds.</description></property>
DataNode 掉线时限参数设置
DateNode 以固定周期向 NameNode 发送心跳,NameNode 如果一段时间内没有收到心跳,就会标记 DateNode 为故障宕机,这段时间叫做 DataNode 超时时长。
timeout = rtbeat.recheck.interval + 10 * dfs.heartbeat.interval
- DataNode 核查心跳间隔时长(默认 5 分钟)
<property> <name>dfs.namenode.heartbeat.recheck-interval</name> <value>300000</value> <description>This time decides the interval to check for expired datanodes. With this value and dfs.heartbeat.interval, the interval of deciding the datanode is stale or not is also calculated.The unit of this configuration is millisecond. </description></property>
- DataNode 心跳检测间隔时长(默认 3 秒)
<property> <name>dfs.heartbeat.interval</name> <value>3</value> <description>Determines datanode heartbeat interval in seconds. Can use the following suffix (case insensitive): ms(millis), s(sec), m(min), h(hour), d(day) to specify the time (such as 2s, 2m, 1h, etc.). Or provide complete number in seconds (such as 30 for 30 seconds). </description></property>
DataNode 目录结构
与 NameNode 不同,DataNode 存储目录是初始阶段自动创建的,不需要额外的格式化
-
VERSION(datanode/current/)
[root@node-01 data]# cat datanode/current/VERSIONstorageID=DS-89dfa113-a168-4eed-87c5-c25d024e49e0clusterID=CID-a1afedaf-49ed-4219-bae3-7b7268fca624cTime=0datanodeUuid=a39bf511-8b1f-4da8-8c1f-3c4201be87cdstorageType=DATA_NODElayoutVersion=-57
- storageID:磁盘存储 ID
- clusterID:集群 ID (全局唯一)
- cTime:标记 DataNode 存储系统的创建时间,对于刚格式化的存储系统属性值为 0
- datanodeUuid:datanode 的唯一识别码
- storageType:存储类型
- layoutVersion:是一个负数(通常只有 HDFS 增加新特性才会更新这个版本号)
-
VERSION(datanode/current/BP-593175008-192.168.229.21-1616685687133/current)
[root@node-01 data]# cat datanode/current/BP-593175008-192.168.229.21-1616685687133/current/VERSIONnamespaceID=1064465530cTime=1616685687133blockpoolID=BP-593175008-192.168.229.21-1616685687133layoutVersion=-57
- namespaceID:DataNode 首次访问 NameNode 时从 NameNode 获取的 storageID(对每个 DataNode 是唯一的,但对单个 DataNode 中所有存储目录则是相同的),NameNode 可用这个属性来区别不同的 DataNode
- cTime:标记 DataNode 存储系统的创建时间
- blockpoolID:一个 block pool id 用于标识一个 block pool,并且是跨集群的全局唯一
- layoutVersion:一个负整数,通常只有 HDFS 增加新特性才会更新版本号