这是我参与「第四届青训营 」笔记创作活动的第五天
HDFS原理
HDFS基础介绍
- Hadoop体系技术
HDFS在Hadoop生态中起到数据存储的角色
- 数据存储演变
Windows单机文件系统
Linux单机文件系统:BTRFS, ZFS, XFS, EXT4等(目前支持近100种文件系统)
分布式文件系统:大容量、高可靠、低成本
分布式存储系统:对象存储、文件系统、快存储、数据库
- HDFS功能特性
- 分布式:收到GFS启发,用Java实现的开发系统,没有实现完整的POSIX文件系统语义
- 容错:自动处理、规避多种错误场景,例如常见的网络错误,机器宕机等
- 高可用:一主多备模式实现元数据高可用,数据多副本实现用户数据的高可用
- 高吞吐:Client直接从DataNode读取用户数据,服务端支持海量client并行读写
- 可扩展:支持联邦集群模式,DataNode数据可达10W级别
- 廉价:只需要通用硬件,不需要定制高端的昂贵硬件设备
架构原理
- HDFS组件
-
Client/SDK
-
数据块 HDFS中的文件是以数据块的形式存储的,默认最基本的存储单位是128MB的数据块,也就是说,存储在HDFS中的文件都会被分割成128MB一块的数据进行存储,如果文件本身小于128MB,则按照实际大小进行存储,并不占用整个数据库空间。
HDFS的数据块之所以会设置这么大,其目的是减少寻址开销。数据块数量越多,寻址数据块所耗费的时间越多。当然也不会设置过大。
HDFS的每一个数据块默认都有3个副本,分布存储在不同的DataNode中,以实现容错,所有如果某个数据块丢失并不影响数据块的访问。数据块的大小和副本数量可在配置文件中更改。 -
DataNode DataNode才是HDFS中真正存储数据的地方。客户端可以向DataNode请求写入或读取数据块,DataNode还在来自NameNode的指令下执行块的创建、删除和复制,并且周期性的向NameNode汇报数据块的信息。
-
NameNode NameNode是HDFS中存储元数据(元数据就是文件名称,大小和在电脑中的位置)的地方,它将所有文件和文件夹的元素据保存在一个文件系统目录树中,任何元数据的改变,NameNode都会记录。HDFS中的每个文件都会拆分为多个数据块存放,这种文件与数据块的对应关系也存储在文件系统目录树中,由NameNode维护。
NameNode还存储数据块到DataNode的映射信息,这种映射信息包括:数据块存放在哪个DataNode中、每个DataNode保存了哪些数据块。
NameNode会监听DataNode的“心跳”和“块报告”,通过心跳和NameNode保持通讯。 -
SecondNameNode SecondNamenode是对主Namenode的一个补充,对内存的需求和Namenode相同
SecondNamenode会周期地进行fsimage文件的合并,防止edits文件过大,导致Namenode启动时间过长
应该与Namenode部署到不同节点上
- 写数据流程
1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
2)NameNode返回是否可以上传。
3)客户端请求第一个 Block上传到哪几个DataNode服务器上。
4)NameNode返回3个DataNode节点,分别为dn08、dn01、dn06。
5)客户端通过FSDataOutputStream模块请求dn8上传数据,dn8收到请求会继续调用dn01,然后dn01调用dn06,将这个通信管道建立完成。
6)dn08、dn01、dn06逐级应答客户端。
7)客户端开始往dn8上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn08收到一个Packet就会传给dn01,dn01传给dn6;dn08每传一个packet会放入一个应答队列等待应答。
8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
-
读数据流程 1)客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。 -
元数据节点NameNode
-
维护目录树 维护目录树的增删改查操作,保证所有修改都能持久化,以便机器掉电不会造成数据丢失或不一致
-
维护文件和数据快的关系 文件被切分成多个块,文件以数据块为单位进行多副本存放
-
维护文件块存放节点信息 通过接收DataNode的心跳汇报信息,维护集群节点的拓扑结构和每个文件块所有副本所在的DataNode类表。
-
分配新文件存放节点 clent创建新的文件时候,需要有NameNode来确定分配目标DataNode
-
共享数据同步进程JouralNode
两个NameNode为了数据同步,会通过一组称作JournalNodes的独立进程进行相互通信。当active状态的NameNode的命名空间有任何修改时,会告知大部分的JournalNodes进程。standby状态的NameNode有能力读取JNs中的变更信息,并且一直监控edit log的变化,把变化应用于自己的命名空间。standby可以确保在集群出错时,命名空间状态已经完全同步。 -
NameNode工作机制 第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode记录操作日志,更新滚动日志。
(4)NameNode在内存中对数据进行增删改。
第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。
(2)Secondary NameNode请求执行CheckPoint。
(3)NameNode滚动正在写的Edits日志。
(4)将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode。
(5)Secondary NameNode加载编辑日志和镜像文件到内存,并合并。
(6)生成新的镜像文件fsimage.chkpoint。
(7)拷贝fsimage.chkpoint到NameNode。
(8)NameNode将fsimage.chkpoint重新命名成fsimage。
-
数据节点DataNode
- 数据块存取 DataNode需要高效实现对数据在磁盘上的存取
-心跳汇报 把存放在本机的数据块列表发送给NameNode,以便NameNode能维护数据块的位置信息,同时让NameNode确定该节点处于正常存活状态
- 副本复制
数据写入时Pipeline操作
机器故障时补全副本
关键设计
- 分布式系统基础概念
-
容错能力 能够处理绝大部分异常场景,例如服务器宕机、网络异常、磁盘故障、网络超时等。
-
一致性模型 为了实现容错,数据必须多副本存放,一致性要解决的问题是如何保障这多个副本的内容都是一致的。
-
可扩展性 分布式存储系统需要具有横向扩张scale-out的能力。
-
节点体系 常见的有主从模式、对等模式等,不管那种模式,高可用是必须的功能。
-
数据放置 系统是由多个节点组成,数据是多副本存放时,需要考虑数据存放的策略。
-
单机存储引擎 在绝大部分存储系统中,数据都是需要落盘持久化,单机引擎需要解决的是根据系统特点,如何高效得存取硬盘数据。
- NameNode
目录树维护
-
fsimage 文件系统目录树
完整的存放在内存中
定时存放在硬盘上
修改是只会修改内存中的目录树 -
EditLog 目录树修改日志
client更新目录树需要持久化EditLog后才能表示更新成功
EditLog可存放在本地文件系统,也可存放在专用系统上
NameNode HA方案一个关键点就是如何实现EditLog共享
数据放置
-
数据块信息维护 目录树保存每个文件的块id
NameNode维护了每个数据块所在节点信息
NameNode根据DataNode汇报的信息动态维护位置信息
NameNode不会持久化数据块位置信息 -
数据防止策略 新数据存放到那些节点
数据均衡需要怎么合理搬迁数据
3个副本怎么合理放置
- DataNode
-
数据块的硬盘存放 文件在NameNode已分割成block
DataNode以block为单位对数据进行存取 -
启动扫盘 DataNode需要知道本机存放了那些数据块
启动时把本机硬盘上的数据块列表加载到内存中
- HDFS写异常处理 Lease Recovery
-
租约 Client要改修一个文件时,需要通过NameNode上锁,这个锁就是租约(Lease)。
-
场景 文件写了一半,client自己挂掉了。可能产生问题:副本不一致,Lease无法释放
-
解决方法 Lease Recovery
Pipeline Recovery
-
情景 文件写入过程中,DataNode侧出现异常挂掉了。
-
异常出现时机 创建连接时,数据传输时,complete阶段
-
解决方法 Pipeline Recovery
- Client读异常处理
-
情景 读取文件的过程,DataNode侧出现异常挂掉
-
解决方法 节点Failover
-
增强情景 节点半死不过,读取很慢
- 旁路系统
-
Balancer 均衡DataNode的容量(datanode之间)
-
Mover
确保副本放置位置符合策略条件(副本经常分布在不同机架和datanode节点)
- 控制面建设
-
可观测性设施 指标埋点
数据采集
访问日志
数据分析 -
运维体系建设 运维操作需要平台化
NameNode操作复杂
DataNode机器规模庞大
组件控制面API
HDFS高可用高扩展
元数据高可用
服务高可用的需求
-
故障类型 硬件故障
软件故障
认为故障 -
灾难:数据中心级别不可用 机房断电
机房空调停机
机房网络故障、拥塞
故障不可避免,灾难时有发生。如HDFS系统不可用,业务停止的损失极大,所以HDFS系统高可用性至关重要。
-
服务可用性指标 MTTR
MTTF
MTBF -
全年不可用时间 可用性 99.9%,全年8.76小时不可用
可用性 99.99%,全年52.6分钟不可用
可用性 99.999%,全年5.26分钟不可用
5. 高可用形式
服务高可用:冷备份,热备份
故障恢复:人工切换、自动切换
人工的反应、决策时间更长,高可用需要让系统自动决策。HDFS的设计中,采用了中心化的元数据管理节点NameNode,NameNode容易成为故障中的单点。
HDFS主备同步实现
- HDFS NameNode高可用架构
-
组件介绍 ActiveNameNode:主节点,提供服务,生产日志
StandbyNamenode:备节点,消费日志
Zookeeper:为自动选主提高统一协调服务
BookKeeper:提供日志存储服务
ZKFC:NameNode探活,触发主备切换
HA Client:提供自动切换的客户端 edit log:操作日志 -
高可用话题 节点状态怎么保存
操作日志如何同步
如何做到自动切换
-
状态复制 状态机复制是实现容错的常规方法
组件:状态机以及副本,变更日志,共识协议 -
NameNode操作日志
Active生产,Standby(可能有多个)消费-物理日志和逻辑日志
日志系统
高可用
高扩展
高性能
强一致(有序)
4. NameNode快状态维护
-
回顾 DataNode Heartbeat,DataNode Block Report
-
区别 Active 即接收,也发起更改; Standby只接收,不发起变更
-
Content Stale 状态 主备切换后,避免DN的不确定状态
HDFS 自动主备切换
-
分布式协调组件-Zookeeper 一般用于提供主、协调、元数据存储
使用它的组件:HDFS、YARN、HBase、Kafka、ClickHouse
HA 核心机制: Watch -
自动主备切换流程
- Service侧 ZKFialoverController:作为外部组件,驱动HDFS NameNode的主备切换,具有如下功能:主备切换、轮询探活、脑裂问题、Fence机制等、
- Client侧 核心机制:StandbyException Client自动处理
日志系统BookKeeper
- BookKeeper架构
BookKeeper存储日志
低延迟
持久性
强一致性
读写高可用 - Quorum机制
多副本一致性读写
场景:多副本对象存储,用版本号表示数据新旧
规则 1、 2、
Sloppy Quorum机制:适用于顺序追加,只写
Write Quorum:写入副本数
Ack Quorum:响应副本数
3. Ensemble机制
Round-Robin Load Balancer
第一轮:1,2,3
第二轮:2,3,4
第一轮:3,4,1
第一轮:4,1,2
优势:数据均衡
数据存储高可用
- 单机存储 RAID
Redundant Array of Independent Disks RAID
特点:廉价、高性能、大容量、高可用 - RAID方案
单机存储数据高可用机制
条带状,冗余,容错校验
nng'a方案
HDFS数据高可用机制
-
HDFS多副本 Hadoop多副本放置
优点
读写路径简单
副本修复简单
高可用 -
HDFS Erasure Coding 业界常见Reed Solomon算法
直接保存EC和Stripe(条带化)后保存的EC
和多副本比较:读写速度,成本,修复速度,读写路径的实现
网络架构的高可用
- 网络架构
机架(Rack):放服务器的架子。
TOR(Top of Rack): 机架顶部的交换机。 数据中心(Data Center): 集中部署服务器的场所 - 机架感知
一个TOR故障导致整个机架不可用 VS 降低跨rack流量
trade-off: 一个本地、一个远端
元数据高扩展性
元数据扩展性挑战
- 扩展性挑战
HDFS NameNode是个集中式服务,部署在单个机器上,内存和磁盘的容量、CPU的计算力都不能无限扩展。
scale up Vs scale out
扩容单个服务器的能力
部署多个服务器来服务
挑战
控制空间分裂
DataNode汇报
目录树结构本身复杂 - 常见scale out方案
KV模型的系统可以适用partition
Redis
Kafka
MySQL(分库分表)
三种数据路由方式
服务端侧
路由层
客户端侧
社区解决方案
- BlockPool
解决DN同时服务多组NN的问题
文件服务分层:Namespace、Block Storage
用BlockPool来分区DN的服务:数据块存储、心跳和快上报 - viewfs
Federation架构:将多个不同集群组合起来,对外表表现像一个集群一样
viewfs通过在client-side的配置指定不同的目录访问不同的NameNode。
局限性:运维复杂
字节解决方案-NNProxy
- NNproxy
NNProxy是ByteDance自研的HDFS代理层,提供路由服务,开源地址:github.com/bytedance/n…
NNProxy主要实现了路由管理和RPC转发,以及授权,限流,查询缓存等额外能力。
数据存储高扩展性
超大集群的长尾问题
- 延迟的分布和长尾延迟
延迟的分布
用百分数来表示访问延迟的统计信息
例如p95表示1ms,代表95%的请求延迟低于1ms,但是后5%延迟会大于1ms 长尾延迟:尾部(p99/p999/p9999)的延迟,衡量系统最差的请求情况,会显著差于平均值 - 尾部延迟放大
木桶原理
尾部延迟放大:访问的服务变多,尾部的请求就会越发的慢。
如何变慢
如何固定延迟阈值,固定延迟百分位 - 长尾问题表现-慢节点
慢节点:读取速度过慢,导致客户端堵塞。
慢节点的发生难以避免和预测
共享资源,后台维护活动,请求多级排队,功率限制
固定的损耗:机器损耗率
混沌现象
离线任务也会遇到长尾问题
全部任务完成时间取决于最慢的任务什么时候完成
集群规模变大,任务数据量变大(集群扩大10倍,问题扩大N(>10)倍)
只要任何数据块的读取受到长尾影响,整个任务就会因此停滞。
超大集群可靠性问题
-
超大集群的可靠性 条件一:超大集群下,有一部分机器是损坏来不及修理的
条件二:副本放置策略完全随机
条件三:DN的容量足够大
推论:必然有部分数据全部副本在损坏的机器上,发生数据丢失 -
CopySet 将DataNode分为若干个Copyset选块在copyset内部选择
原理:减少副本放置的组合数,从而降低副本丢失的概率
超大集群的不均匀问题
- 超大集群的负载均衡和数据迁移
-
数据写入不均 数据不均匀
节点容量不均匀
数据新旧不均匀
访问类型不均匀
资源负载不均匀 -
DN冷热不均 数据不均匀
节点容量不均匀
数据新旧不均匀
访问类型不均匀
资源负载不均匀 -
负载均衡和数据迁移的典型场景
数据迁移工具
- 跨NN迁移
DistCopy
基于MapReduce,通过一个个任务,将数据从一个NameNode拷贝到另一个NameNode
需要拷贝数据,流量较大,速度较慢
FastCopy
开源社区的无需拷贝数据的快速元数据迁移方案
前提条件:新旧集群的DN列表吻合
对于元数据,直接复制目录树的结构和块信息
对于数据块,直接要求DataNode从源Blockool hardlink到目标BlookPool,没有数据拷贝
hardlink:直接让两个路径指向同一块数据
- Balancer
工具向DataNode发起迁移命令,平衡各个DataNode的容量
场景:单机房适用,多机房使用,限流措施
评价标准:稳定性成本,可运维性,执行效率