HDFS高可用和高扩展机制分析 | 青训营笔记
这是我参与「第四届青训营」笔记创作活动的的第7天。
一、课程概述
- HDFS元数据服务的高可用
- HDFS数据存储的高可用
- HDFS元数据服务的高扩展
- HDFS数据存储的高扩展
二、详细内容
1. 元数据高可用
主备系统:基于日志、自动切换、实时热备
1.1 高可用的需求
1.1.1 高可用定义
系统在困境中仍能正常工作的性能。
- 故障 adversity
- 硬件故障
- 软件故障
- 人为错误
- 灾难:数据中心级别不可用
- 机房断电
- 机房空调停机
- 机房网络故障、阻塞
- 正常工作
- 正确完成功能
- 能达到期望的性能水准
- 容灾 在相隔较远的异地建立两套或多套功能相同的系统,互相之间可以进行健康状态监视和功能切换,当一处系统因意外停止工作时整个应用系统可以切换到另一处,使得该系统功能可以正常运转。
1.1.2 故障度量指标
- 恢复时常:故障发现->故障定位->修复->测试->恢复
- MTTR: mean time to repair 修复->测试->恢复
- MTTF: mean time to failure
- MTBF: mean time between failures
1.1.3 可用性年化
- 全年不可用时间 (可用性百分比)
- 99.9% 全年8.76小时不可用
- 99.99% 全年52.6分钟不可用
- 99.999% 全年5.26分钟不可用
1.1.4 高可用的形式
- 服务高可用
- 热备份
- 冷备份
- 故障恢复操作:人工反应决策时间更长,高可用系统需要自动决策
- 人工切换
- 自动切换
- HDFS设计中采用了中心化的源数据管理节点NameNode,因此容易成为故障中的单点
1.2 HDFS主备同步实现
1.2.1 HDFS NameNode高可用架构
- 组件介绍
- ActiveNameNode:主节点,提供服务,生产日志
- StandbyNameNode:备节点,消费日志
- ZooKeeper:为自动选主提供同一协调服务
- BookKeeper:提供日志存储服务
- ZKFC:NameNode探活、触发主备切换
- HA Client:提供了自动切换的客户端
- EditLog:操作的日志
- 三个问题
- 节点状态如何更新
- 操作日志如何同步
- 如何做到自动切换
1.2.2 理论基础 - 状态机复制和日志
状态机复制是实现容错的常规方法。
- 组件
- 状态机以及其副本
- 变更日志
- 共识协议:所有状态机收到相同的变更日志和指令等
1.2.3 NameNode操作日志的生产消费
- 目录树和文件信息的更新
- active生产,standby消费
- 物理日志和逻辑日志
- 逻辑日志:保存实际指令
- 物理日志:INode XXX数据变为XXX等
- 日志系统
- 高可用
- 高扩展
- 高性能
- 强一致(有序)
1.2.4 NameNode块状态维护
-
DataNode Heartbeat:DataNode使用心跳向active和standby节点都报备存活
-
DataNode Block Report:block汇报同时到active和standby
-
区别
- active:只接受,也发起变更
- standby:只接受但不发起变更
-
content stale状态:主备切换后避免dataNode的不确定状态
- 新上主的node不能直接进行数据操作,需要等上报状态后再进行
1.3 HDFS自动主备切换
1.3.1 分布式协调组件-ZooKeeper
一般用于提供选主、协调、元数据存储
- 使用它的组件
- HDFS、YARN、HBase
- Kafka、ClickHouse
- HA核心机制:watch
- 当节点状态发生变化时 zookeeper会进行统治
- 当节点没有心跳时,会对节点进行删除
1.3.2 自动主备切换流程
-
Server侧
- ZKFC zookeeperFailoverController:作为外部组件驱动HDFS NameNode的主备切换
- 轮训探活:定时向Server发送RPC请求检查状态
- 发现问题:对应服务器的zookeeper检查状态无回应、不正确、节点掉线,发起主备切换
- 脑裂问题:多节点认为自己都是active
- Fence机制:bookkeeper阻止多节点写同一日志 在老版本中需要手动阻止
-
Client侧
- 核心机制:StandbyException
- 依次轮询发送,接受standbyException,直至寻找到Active NameNode
- Client自动处理
- 核心机制:StandbyException
1.4 Bookkeeper架构
1.4.1 bookkeeper基础
依靠写日志的方式存放状态。 DataOperation存在bookkeeper cluster的bookie上。 MetaDataOperation写在Zookeeper cluster的节点上。
- bookkeeper存储日志
- 低延时
- 持久性
- 强一致性
- 读写高可用
- 日志系统和文件系统复杂度
1.4.2 Quorum机制
- quorum:多副本一致性的读写
- 多副本对象存储,用版本号表示数据新旧
- 日志场景比对象保存更简单
- 不会对对象进行反复修改
1.4.3 Bookkeeper Quorum
- sloppy quorum
- 日志顺序追加,只写入
- write quorum 写入副本数:不需同时写完ACK
- ack quorum 响应副本数:只需要部分副本响应即可
1.4.4 Bookkeeper Ensemble
- Ensemble机制:round-robin load balancer
- 4机器写入3副本:第一轮123,第二轮234,第三次341,第四次412...
- 轮流写入,数据均衡
2. 数据存储高可用
2.1 单机存储的数据高可用机制
2.1.1 RAID:redundant array of independent disks
- 将多块设备对外提供逻辑上的一块设备
- 特点:廉价/高性能/大容量/高可用
2.1.2 RAID方案
- RAID0:条带化(将实际数据分割成一块一块的条带,写在不同的盘上)
- RAID1:冗余(在同一数据写在两块磁盘上 问题:数据不一致?)
- RAID3:容错校验(根据不同数据条带计算出校验码存放在单独的盘上,可以根据该校验码反推)
2.2 HDFS数据高可用机制
2.2.1 HDFS多副本
- HDFS版本的RAID1
- 将同一块数据存放在多个dataNode上,若单个dataNode故障有其他dataNode持有该数据
- 若发现checksum不对,则知道该块上的数据错误,此时NameNode则会找正确的dataNode传输相应块到改错误块上
- 优点
- 读写路径简单
- 副本修复简单:从其他Node拷贝
- 高可用:三副本很难同时损坏,丢块
- 缺点
- 三副本在大数据下存储成本过高
2.2.2 Erasure coding
- HDFS版本的RAID2
- Reed Solomon算法
- 【单位矩阵+额外行矩阵块】*【数据源】= CodeWord (Data+Parity)
- Data:数据/Parity:纠善码
- 和多副本比较
- 读写速度:按照块读取,计算后速度慢
- 切分成条带化 striping
- 成本:成本更优 容忍丢失更少的机器
- 修复速度:引用更多计算
- 读写路径的实现:比存原始数据更复杂
- 读写速度:按照块读取,计算后速度慢
2.3 考虑网络架构的数据高可用
2.3.1 网络架构
- server 一台服务器
- rack 机架:放服务器的架子
- TOR(top of rack)机架顶部的交换机
- POD(point of delivery)
2.3.2 副本放置策略 - 机架感知
- 一个TOR故障导致整个机架不可用 vs 降低跨rack流量
- trade-off 一个本地一个远端
- 放置strategy
- 一个副本写在local node
- 第二个写在remote上
- 第三个写在同一个remote rack上
- 更多副本随机写
- 缺点:仍然有两份数据在同一机架上
3. 元数据高扩展性
3.1 元数据扩展性挑战
3.1.1 元数据扩展问题
-
HDFS NameNode是集中式服务,部署在单个机器上,内存和磁盘的容量、CPU的计算力都不能无限扩展
-
scale up:扩容单个服务器的能力
-
scale out:部署多个服务器来服务
-
挑战:
- Namespace分裂:目录树分裂
- DataNode汇报:NameNode如何负责DataNodes
- 目录树结构本身复杂
3.1.2 常见的scale out方案
- KV模型系统可以使用partition
- Redis/Kafka/MySQL
- 三种数据路由方式
- 服务端侧
- 路由层
- 客户端侧
3.2 社区的解决方案
3.2.1 BlockPool
- 解决DN服务多组NN的问题
- 同一个block id 在不同的NN上出现
- 文件服务分层
- namespace
- block storage:存储哪些块,块在哪里
- 每个nameNode有单独的BlockPool,对应pool对应block
- 用blockpool来区分DN服务,block id变成块id+pool id
- 数据块存储
- 心跳和块上报
3.2.2 viewfs
- Federation架构:将多个不同集群组合起来对外表现像一个集群一样
- viewfs通过在client-side配置,制定不同的目录访问不同的NameNode
- 运维复杂
3.3 字节NNProxy方案
3.3.1 NNProxy
- client向NNProxy发送,Proxy转接访问对应NameNode
- 实现了路由管理和RPC转发
- 以及鉴权、限流、查询缓存等额外能力
3.3.2 NNProxy路由规则保存
- 考虑扩展性和运维性
- 服务侧端困难,客户端运维复杂 -> 路由层
- 使用zookeeper存储不同NameNode的proxy
- 定期更新、转发proxy
3.3.3 NNProxy路由转发实现
- 路径最长匹配规则,可以进一步划分目录树
- 单NN瓶颈 - 类似NameNode单点热点
- 无法跨集群rename:目录树不同阶层在不同集群上
4. 存储数据高扩展性
4.1 超大集群的长尾问题
4.1.1 延迟分布与长尾延迟
- 延迟的分布
- 用百分数来表述访问延迟的统计特征
- p95 延迟1ms 代表95%请求延迟低于1ms但后5%请求延迟大于1ms
- 长尾延迟:尾部p99/p999的延迟,衡量系统最差的请求的情况,会显著的差于平均值
4.1.2 尾部延迟放大
- 木桶原理:响应时间取决于最慢的请求响应时间
- 访问的服务变多,尾部请求越来越慢
- 如何变慢
- 固定延迟阈值
- 固定延迟百分位
4.1.3 长尾问题表现 - 慢节点
- 慢节点:某节点读取速度过慢,导致客户端阻塞
- 发生难以避免和预测
- 共享资源,后台维护,请求多级排队,功率限制
- 固定损耗:机器损坏率
- 混沌现象
- 离线任务也会遇到长尾问题
- 全部任务完成时间取决于最慢的任务完成时间
- 集群规模变大任务的数据量也变大
- 任何数据块读取收到长尾影响,整个任务因此停滞
- 集群扩大10倍 问题扩大N>10倍
4.2 超大集群的可靠性问题
4.2.1 数据可靠性
- 必然有部分数据全部副本在损坏机器上,发生数据丢失
- 超大集群下一部分机器损坏来不及修理
- 副本放置策略随机
- DN容量足够大
- 叠加长尾问题导致整个任务无法执行
4.2.2 copyset
- 将DN分为若干个copyset选块在copyset内选择
- 将全随机副本放置变为规律性,减少副本放置组合数,从而降低副本丢失概率
- 仅需在copyset内进行修复,提高速度
4.3 超大集群的不均匀问题
-
负载均衡:可靠性/降低成本/避免热点
-
数据写入不均
- 数据不均匀
- 节点容量不均匀
- 数据新旧不均匀
- 访问类型不均匀
- 资源负载不均
- 数据不均匀
-
数据读取不均
- 数据不均匀
- 节点容量不均匀
- 数据新旧不均匀
- 访问类型不均匀
- 资源负载不均
- 数据不均匀
-
访问类型不均匀
-
目标:容量/访问低昂/数据冷热/承载能力
-
典型场景: DN上下线/机房均衡/日常打散
4.4 数据迁移工具速览
4.4.1 元数据迁移
- DistCopy:基于mapreduce通过任务将数据从一个NN拷贝到另一个NN
- 拷贝数据,流浪大,速度慢
- FastCopy
- 前提:新旧集群DM列表吻合
- 元数据直接在NN集群间拷贝,数据在DN上不同blockpool进行hardlink,不需要数据复制
4.4.2 数据迁移
- Balancer
- 工具向DataNode发起迁移指令,平衡各个DN的容量
- 场景:单/多机房、限流措施
- 评价:稳定性成本等
三、实践例
- 字节HDFS多机房实践
- 多机房解决的问题
- 容量问题:物理问题、场地不足
- 容灾问题:多地部署
- HDFS双机房放置的设计
- 写入时每个数据块在两个机房各至少有一个副本,数据实时写入到两个机房
- 读取时优先读本地副本,避免大量跨机房读取
- 多机房部署组件支持
- zookeeper在不同机房各部署一个,额外部署一个单独的zookeeper做调解
- bookkeeper在不同机房各部署一个
- NameNode/DataNode需要一定数量
- 容灾期间策略
- 限制跨机房写入
- 限制跨机房副本复制
- 写在数据中心
- 小文件问题LSOF
- 大小不到一个HDFS block大小的文件过多
- NameNode成为瓶颈
- IO变为小随机IO 数据访问缓慢
- 计算任务启动慢
- 解决方法
- 后台任务合并小文件
- shuffle service:mapreduce中间数据缓存在内存,聚合合并
四、课后总结
- 难点问题
- HDFS如何实现高可用和高扩展:副本存储、NNProxy等
- 多副本的问题:保证一致性
- 扩展性问题:长尾效应——短板
- 个人总结:本节课主要学习了HDFS系统如何实现高可用和高扩展的机制,以及对应的问题。HDFS比较复杂,需要更多基础上的理解。