这是我参与「第四届青训营」笔记创作活动的第11天
本节课程目录:
- HDFS 元数据高可用
- HDFS 数据存储高可用
- HDFS 元数据高扩展性
- HDFS 数据存储高扩展性
1. HDFS 元数据高可用
1.1 高可用的需求
1.1.1 服务高可用的需求
- 故障类型:
- 硬件故障
- 软件故障
- 人为故障
- 灾难:数据中心级别不可用
- 机房断电
- 机房空调停机
- 机房网络故障堵塞
故障不可避免 灾难时有发生
- 如果 HDFS 系统不可用 业务停止的损失极大
- 无法核算广告账单 直接引发收入损失
- 无法生产数据报表 数据驱动无从谈起
- 无法进行模型训练 用户体验越来越差
1.1.2 高可用的衡量
服务可用性指标
- MTTR(Mean Time To Repair)
- 平均修复时间,系统能多快恢复
- MTTF(Mean Time To Failure)
- 平均失效时间,运行到故障间的时间,一般用于不可修复的系统(制造业)。
- MTBF(Mean Time Between Failures)
- 平均无故障时间,两次故障间的间隔,一般用于可修复的系统(软件)。
1.1.3 可用性的年化
可用性计算:
1.1.4 高可用的形式
服务高可用
- 冷备份:备份服务的数据,可以和数据归档相结合。在主服务故障时,利用备份的数据重启。
- 热备份:主服务和备服务同时运行,在主服务故障时,随时可以切换到备服务。
故障恢复操作
- 人工切换:在故障发生时,运维人员接收报警后,手动执行服务切主操作。一般较慢,难以满足全年不可用时间的目标。
- 自动切换:通过探活组件、分布式共识协议等手段,系统能自动发现主服务的故障,并切换到备份不符。
1.2 HDFS 主备同步实现
1.2.1 HDFS NameNode 高可用架构
- Active NameNode:提供服务的 NameNode 主节点,生产 editlog。
- Standby NameNode:不提供服务,起备份作用的 NameNode 备节点,消费 editlog
- ZooKeeper:开源的分布式协调组件,主要功能有节点注册、主节点选举、元数据存储。
- BookKeeper:开源的日志存储组件,存储 editlog
- ZKFC:和 ZK、NN 通信,进行 NN 探活和自动主备切换。
- HA Client:处理 StandbyException,在主备节点间挑选到提供服务的主节点。
- EditLog:操作的日志
1.2.2 理论基础 - 状态机复制和日志
状态机复制模型:实现容错服务的一种常规方法,主要通过复制服务器,并协调客户端和这些服务器镜像间的交互来达到目标。这个方法也同时提供了理解和设计复制管理协议的一套基本框架。
- 状态机:一个状态机从“初始”状态开始,每一个输入都被传入转换函数和输出函数,以生成一个新的状态和输出。在新的输入被接收到前,状态保持不变,而输出同时被传输给恰当的接受者。
- 状态机复制:确定性的状态机具有「处理确定的输入后,状态唯一确定」的特性。状态机复制利用这个特性实现多个相同的状态机副本的同步更新。
- 变更日志:触发状态机更新的变更操作,具有全局确定的顺序。
- 共识协议:确保每个副本都能收到相同的日志的共识协议,常见的有 Paxos、Raft、ZAB。
1.2.3 NameNode 状态持久化
Checkpoint 机制:将旧的 FSImage 和 EditLog 合并生成新的 FSImage 的流程,在完成后旧的数据可以被清理以释放空间。
- FSImage 文件:较大的状态记录文件,是某一时刻 NN 全部需要持久化的数据的记录。大小一般在 GB 级别。
- EditLog 文件:是某段时间发生的变更日志的存储文件。大小一般在 KB~MB 级别。
1.2.4 NameNode 操作日志的生产消费
- Active 生产 Standby 消费
- 物理日志:存储了物理单元(一般是磁盘的 page)变更的日志形式。
- 逻辑日志:存储了逻辑变更(例如 rename /a to /b)的日志形式。
- 日志系统
- 高可用
- 高扩展性
- 高性能
- 强一致有序
1.2.5 NameNode 块状态维护
区别:
- Active 即接收,也发起变更
- Standby 只接收,不发起变更
Content Stale 状态
- 主备切换后,避免 DN 的不确定状态
1.3 HDFS 自动主备切换
1.3.1 分布式协调组件 - ZooKeeper
- 一般用于提供选主、协调、元数据存储。
- ZooKeeper 是广泛使用的选主组件,它通过 ZAB 协议保证了多个 ZK Server 的状态一致,提供了自身的强一致和高可用。
- ZooKeeper 的访问单位是 znode,并且可以确保 znode 创建的原子性和互斥性(CreateIfNotExist)。client 可以创建临时 znode,临时 znode 在 client 心跳过期后自动被删除。
- ZK 提供了 Watch 机制,允许多个 client 一起监听一个 znode 的状态变更,并在状态变化时收到一条消息回调(callback)。
- 基于临时 znode 和 Watch 机制,多个客户端可以完成自动的主选举。
1.3.2 自动主备切换流程 - Server 侧
- ZKFailoverController
- 作为外部组件,驱动 HDFS NameNode 的主备切换
- 一般和 NN 部署在一起的进程,负责定时查询 NN 存活和状态、进行 ZK 侧主备选举、执行调用 NN 接口执行集群的主备状态切换、执行 fence 等能力。
1.3.3 自动主备切换流程 - Client 侧
- 核心机制
- StandbyException
1.4 日志系统 BookKeeper 简介
1.4.1 BookKeeper 架构
- BookKeeper 存储日志
- 低延时:整个集群的读写能力可以随着添加存储节点 Bookie 而扩展
- 持久性:数据写入多个存储节点,数据写入就不会丢失
- 强一致性:日志系统是追加写入的形式,Client 和日志系统的元数据可以明确目前已经成功的写入日志的序号
- 读写高可用:日志存储本身是高可用的。因为日志流比文件系统本身的结构更为简单,日志系统高可用的实现也更为简单。
1.4.2 Quorum 机制
- Quorum 机制
- 多副本一致性读写
- 基于鸽巢原理 在多个副本间确保高可用、高性能的多副本变更协议
- 场景
- 多副本对象存储 用版本号标识数据新旧
- 高可用:多个副本确保了高可用
- 高性能:不用等所有副本写入成功,降低了的长尾延迟
日志场景比对象保存更简单
1.4.3 Bookkeeper Quorum
- Sloppy Quorum 机制
- 基于 Quorum 的多数派思想来提供高可用、高性能写入的日志写入
- 场景
- 日志写入是追加,不是状态变更,只需要确认目前的 entry-id,相对更简单
- Write Quorum:一次写入需要写入到的存储节点数
- Ack Quorum:一次写入需要收到的响应数,小于 write quorum
- 高性能:不用等所有副本写入成功,降低了的长尾延迟
- Ensemble:通过轮询来确认 write quorum 实际对应的存储节点实例,可以比较简单的完成副本放置和扩展
1.4.4 Bookkeeper Ensemble
优势:数据均衡
2. 数据存储高可用
2.1 单机存储的数据高可用机制
2.1.1 回到单机存储 - RAID
Redundant Array of Independent Disks
提供 RAID 功能的 NAS 设备
2.1.2 RAID 方案讲解
RAID:将多个廉价、不可靠、低性能、容量小的磁盘组装在一起,提供高可靠、高性能、大容量逻辑磁盘服务的一组磁盘列阵方案。
- RAID 0 :将数据分块后按条带化的形式分别存储在多个磁盘上,提供大容量、高性能。
- RAID 1:将数据副本存储在多个磁盘上,提供高可靠
- RAID 3:在数据分块存储的基础上,将数据的校验码存储在独立的磁盘上,提供高可靠、高性能。
2.2 HDFS 的数据高可用机制
2.2.1 HDFS 多副本
HDFS 版本的 RAID 1 将数据块存储在多个 DN 上
Hadoop 的多副本放置
- 优点
- 读写路径简单
- 副本修复简单
- 高可用
2.2.2 Erasure Coding 原理
HDFS 版本的 RAID 2/3 业界常用 Reed Solomon 算法
Reed Solomon 算法原理
2.2.3 HDFS Erasure Coding
将数据分段,通过特殊的编码方式存储额外的校验块,并条带化的组成块,存储在 DN 上。
和多副本相比
- 成本更低
- 实现读写路径更复杂
2.3 考虑网络架构的数据高收入
2.3.1 网络架构
- 机架/机柜:将几个服务器统一供电、提供对外网络的固定的物理设备。
- TOR(top of rack):机架顶部(或底部)的交换机,负责机架内服务器和数据中心的其他服务器的网络通信。
- 机房和数据中心都是指大量服务器集中放置的场所。
- 机房:强调的基础设施建设,例如物理承重、空调、防水、消防。
- 数据中心:强调机房的业务属性。
- 网络拓扑:按数据中心->机架->机器的顺序,描述进程在网络空间中所处的位置。
- 跨机房专线:由网络服务商提供,连接机房的专用网络。
- 稳定性和安全性好于公网。
- 相比于数据中心内网络,吞吐更为有限、延迟更高、成本更高。
2.3.2 副本放置策略 - 机架感知
以 TOR 为关键点,机架是一个故障域。数据副本全部放置在一个机架中,当相应 TOR 故障时数据就无法访问
2.4 案例分析
2.4.1 字节跳动的 HDFS 多机房实践
HDFS 集群从单机房演进到双机房,再从双机房演进到更多的机房。
多机房能够解决容量问题和容灾问题
HDFS 双机房放置的设计:
- 写入时,每个数据块在两个机房至少各有一个副本,数据实时写入到两个机房
- 读取时,优先读本地的副本,避免了大量的跨机房读取
2.4.2 多机房容灾实践
多机房部署的组件:ZooKeeper、BookKeeper、NameNode、DataNode
容灾期间的策略
- 容灾期间 限制跨机房写入
- 容灾期间 限制跨机房副本复制
3. 元数据高扩展性
3.1 元数据扩展性挑战
3.1.1 元数据节点扩展性的挑战
HDFS NameNode 是个集中式服务,部署在单个机器上,内存和磁盘的容量、CPU的计算力都不能无限扩展
扩展性方案
- scale up:通过单机的 CPU、内存、磁盘、网卡能力的提升来提升系统服务能力,受到机器成本和物理定律的限制。
- scale out:通过让多台机器组成集群,共同对外提供服务来提升系统服务能力。一般也称为高扩展、水平扩展。
挑战
- 名字空间分裂
- DataNode 汇报
- 目录树结构本身复杂
3.1.2 常见的 Scale Out 方案
partition 方法
- 水平分区和垂直分区:水平分区指按 key 来将数据划分到不同的存储上;垂直分区指将一份数据的不同部分拆开存储,用 key 关联起来。partition 一般都水平分区,又称 shard。
- 常用于 KV 模型,通过 hash 或者分段的手段,将不同类型 key 的访问、存储能力分配到不同的服务器上,实现了 scale out。
- 重点:不同单元之间不能有关联和依赖,不然访问就难以在一个节点内完成。例如 MySQL 的分库分表方案,难以应对复杂跨库 join。
3.2 社区的解决方案
3.2.1 BlockPool
- 将文件系统分为文件层和块存储层,对于块存储层,DN 集群对不同的 NN 提供不同的标识符,称为 block pool。
- 解决了多个 NN 可能生成同一个 block id,DN 无法区分的问题。
3.2.2 viewfs
Federation 架构
- 使得多个集群像一个集群一样提供服务的架构方法,提供了统一的服务视图,提高了服务的扩展性。
- 文件系统的目录树比 kv 模型更复杂,划分更困难。
- 邦联架构的难点一般在于跨多个集群的请求,例如 HDFS 的 rename 操作就可能跨多个集群。
viewfs 通过在 client-side 的配置,指定不同的目录访问不同的 NameNode
3.3 字节跳动的 NNProxy 方案
3.3.1 字节跳动的 NNProxy
NNProxy 是 ByteDance 自研的 HDFS 代理层,主要提供了路由管理、RPC 转发,额外提供了鉴权、限流、查询缓存等能力。开源社区有类似的方案 Router Based Federation,主要实现了路由管理和转发。
3.3.2 NNProxy 路由规则保存
3.3.3 NNProxy 路由转发实现
路径最长匹配规则
3.4 案例:小文件问题
小文件问题(LSOF) 不到一个 HDFS BLock 大小的文件过多
- 元数据问题:多个小文件相对于一个大文件,使用了更多元数据服务的内存空间。
- 数据访问问题:多个小文件相对于一个大文件,I/O 更加的随机,无法顺序扫描磁盘。
- 计算任务启动慢:计算任务在启动时,一般会获得所有文件的地址来进行 MapReduce 的任务分配,小文件会使得这一流程变长。
- 典型的 MR 流程中,中间数据的文件数和数据量与 mapper * reducer 的数量成线性,而为了扩展性,一般 mapper 和 reducer 的数量和数据量成线性。于是,中间数据的文件数和数据量与原始的数据量成平方关系。
- 解决方案
- 小文件合并任务:计算框架的数据访问模式确定,可以直接将小文件合并成大文件而任务读取不受影响。通过后台运行任务来合并小文件,可以有效缓解小文件问题。通过 MapReduce/Spark 框架,可以利用起大量的机器来进行小文件合并任务。
- Shuffle service:shuffle 流程的中间文件数是平方级的,shuffle service 将 shuffle 的中间数据存储在独立的服务上,通过聚合后再写成 HDFS 文件,可以有效地缓解中间数据的小文件问题
4. 存储数据高扩展性
4.1 超大集群的长尾问题
4.1.1 延迟的分布和长尾延迟
百分位延迟
- 将所有请求的响应速度从快到慢排序,取其中某百分位的请求的延迟时间。
- 例如 pct99 代表排在 99% 的请求的延迟。相对于平均值,能更好的衡量长尾的情况。
长尾延迟 尾部(p99/p999/p9999)的延迟,衡量系统最差的请求情况,会显著的差于平均值。
4.1.2 尾部延迟放大
- 木桶原理:并行执行的任务的耗时取决于最慢的一个子任务。
- 尾部延迟放大:一个请求或任务需要访问多个数据节点,只要其中有一个慢,则整个请求或任务的响应就会变慢。
- 固定延迟阈值,访问的集群越大, 高于该延迟的请求占比越高。
- 固定延迟百分位,访问的集群越大,延迟越差。
4.1.3 长尾问题的表现 - 慢节点
慢节点 读取速度过慢,导致客户端阻塞
慢节点问题:网络不会直接断联,而是不能在预期的时间内返回。会导致最终请求不符合预期,而多副本机制无法直接应对这种问题。
- 共享资源、后台维护活动、请求多级排队、功率限制
- 固定的损耗:机器损坏率
- 混沌现象
高负载:单个节点处理的请求超过了其服务能力,会引发请求排队,导致响应速度慢。是常见的一个慢节点原因。
- 全部任务完成时间取决于最慢的任务什么时候完成
- 集群规模变大,任务数据量也变大
- 只要任何数据块的读取收到长尾影响,整个任务都会停滞
4.2 超大集群的可靠性问题
4.2.1 超大集群下的数据可靠性
- 条件一:超大集群下,有一部分机器是损坏来不及修理的
- 条件二:副本放置策略完全随机
- 条件三:DN 的容量足够大
必然有部分数据全部副本在损坏的机器上发生数据丢失
4.2.2 Copyset
降低副本放置的组合数,降低副本丢失的发生概率。
修复速度:DN 机器故障时,只能从少量的一些其他 DN 上拷贝数据修复副本。
4.3 超大集群的不均匀问题
4.3.1 超大集群的负载均衡和数据迁移
4.3.2 数据写入不均
- 数据的不均匀
- 节点容量不均匀:机器上的数据量不均衡。
- 原因可能是各种复杂情况导致,归根结底是混沌现象。
- 数据新旧不均匀:机器上的数据新旧不均匀。
- 例如:新上线的机器,不做任何数据均衡的情况下,只会有新写入的数据。而一般新数据更容易被读取,更为「热」。
- 访问类型不均匀:机器上的数据访问类型不均。
- 例如:机器学习训练需要反复读取数据,小 I/O 更多。而大数据场景一般只扫描一次,大 I/O 为主。这两种模式的读写比不同,I/O pattern 不同,就来带访问冷热的不同。
- 节点容量不均匀:机器上的数据量不均衡。
DN 的写入量不均匀
4.3.3 DN 冷热不均
DN 的访问不均匀
4.3.4 负载均衡和数据迁移的典型场景
- DN 上线:新上线的机器没有任何数据,而且只会有新数据写入。需要迁移其他 DN 的旧数据到新 DN 上,使得负载和数据冷热均衡。
- DN 下线:需要下线的机器,需要提前将数据迁移走再停止服务,避免数据丢失的风险。
- 机房间均衡:因为资源供应、新机房上线等外部条件,机房规划、业务分布等内部条件,不同机房的资源量和资源利用率都是不均衡的。需要结合供应和业务,全局性的进行资源均衡。
- 日常打散:作为日常任务运行,不断地从高负载、高容量的机器上搬迁数据到低负载、低容量的机器上,使得整个集群的负载均衡起来。
4.4 数据迁移工具速览
4.4.1 数据迁移工具 - 跨 NN 迁移
- DistCopy
- 通过 MapReduce 任务来并行迁移数据,需要拷贝数据和元数据。
- 需要拷贝数据,网络流量较大,速度较慢。
- FastCopy
- 开源社区的无需拷贝数据的快速元数据迁移方案
- 前提条件:新旧集群的 DN 列表吻合
- 基于 hardlink 和 blockpool 的原理
- 元数据直接在 NN 集群间拷贝,而数据则在 DN 上的不同 blockpool(对应到 NN 集群)进行 hardlink,不用数据复制。
- 迁移速度要大大优于 DistCopy。
4.4.2 数据迁移工具 - Balancer
- 代替 NameNode 向 DataNode 发起副本迁移的命令,批量执行副本迁移,平衡各个 DataNode 的容量
- 场景:大规模数据平衡、机器上下线。
- 评价标准
- 稳定性成本
- 可运维性
- 执行效率