深入 HBase 11 - RegionServer 崩溃与 WAL 恢复
前置问题与边界
RegionServer 崩溃以后,HBase 要恢复的是由该 RegionServer 服务的 Region,而不是让 HDFS 自动懂得数据库事务。HDFS 负责保存 WAL 和 HFile;HBase 通过 procedure、assignment、WAL split 或 replay,把未 flush 的 edit 重新送到负责 Region 的服务进程。
本篇回答一个核心问题:ServerCrashProcedure、WAL split/replay 和 Region reassignment 如何把服务恢复到可读写状态。行级一致性边界见第 10 篇;复制链路见第 12 篇;Snapshot 和 Backup 见第 13 篇。
崩溃恢复路径图
flowchart TD
A[RegionServer crash] --> B[Master detects dead server]
B --> C[ServerCrashProcedure]
C --> D[Mark affected regions unavailable]
C --> E[Split or distribute WAL edits]
E --> F[recovered.edits per region]
F --> G[Assign region to live RegionServer]
G --> H[Open region and replay edits]
H --> I[Region accepts traffic]
故障恢复里有三类状态不能混在一起。Region 的服务归属由 HBase 的 assignment 流程推进;WAL 文件位于 HDFS 上,但 edit 如何按 Region 拆分和重放由 HBase 负责;hbase:meta 记录 Region 位置信息和状态,ZooKeeper 只承担协调与注册一类职责。
关键对象和状态
| 对象 | 位置 | 作用 |
|---|---|---|
| ServerCrashProcedure | HMaster procedure 框架 | 编排死亡 RegionServer 的恢复步骤 |
| WAL | HDFS | 保存 RegionServer 写入 edit |
| WAL split | Master/worker 协作流程 | 把死亡 server 的 WAL edit 按 Region 分派 |
recovered.edits |
Region 目录附近 | Region 打开时用于 replay 的 edit 文件 |
| AssignmentManager | HMaster | 推进 Region 关闭、打开和迁移状态 |
hbase:meta |
HBase 系统表 | 保存 Region 位置信息和状态 |
现代 HBase 2.x 的恢复流程由 procedure 框架驱动。文章不应把它写成“Master 直接手工移动目录”或“HDFS 自动恢复数据库状态”。HDFS 只提供持久文件和租约恢复能力;数据库语义来自 HBase 的 WAL 和 Region 打开流程。
WAL 为什么是恢复中心
写入成功但尚未 flush 成 HFile 的数据,只存在于 MemStore 和 WAL 里。RegionServer 进程消失以后,MemStore 随进程丢失;WAL 仍在 HDFS 上。恢复流程要读取死亡 RegionServer 留下的 WAL,把属于不同 Region 的 edit 拆出来,并在 Region 被新 RegionServer 打开时重放。
WAL 记录的是 edit 序列。恢复时要维护 sequence id,避免已经 flush 的 edit 被重复应用,也避免未 flush 的 edit 被跳过。这个边界解释了为什么 WAL、MemStore flush 和 Region 打开状态必须一起看。
模式提炼:把易失状态写成可分区重放日志。公式是 volatile_state + durable_log -> replay(partition) -> serving_state。LSM 数据库、消息队列 consumer checkpoint、流处理状态后端都用类似方法。日志本身不等于服务恢复,必须有分区归属和重放进度。
ServerCrashProcedure 的职责
ServerCrashProcedure 接到死亡 RegionServer 事件后,会围绕该 server 的 Region 和 WAL 做一组步骤:标记 affected Regions、处理 WAL、推进 Region reassignment,并在必要时更新元数据状态。不同版本内部步骤可能调整,但职责边界保持一致:把故障节点上的 Region 交给存活节点,并让未 flush edit 被 replay。
这不是客户端透明重试就能完成的事。客户端只会看到请求失败、重试、重新定位或超时。真正决定数据是否可恢复的,是服务端写入时的 durability、WAL 是否可读、split/replay 是否成功,以及 Region 是否能重新打开。
recovered.edits 的角色
WAL split 后,属于某个 Region 的 edit 会变成该 Region 打开流程可以读取的恢复文件,常见目录名包含 recovered.edits。Region 被新 RegionServer 打开时,HBase 会把这些 edit 应用到内存状态,再继续提供服务。
不要把 recovered.edits 写成普通 HFile。它不是最终 StoreFile,也不是 compaction 的输入;它是 Region 打开时用于重放的恢复资料。重放成功后,后续 flush 会把内存状态写成 HFile。
最小实验
UNVERIFIED_RUNTIME:当前环境未安装 HBase 2.6.6、Hadoop 3.4.3、JDK 17 和 HBase 源码 checkout,以下步骤只作为复现实验,不附带输出。
在 HBase 源码 checkout 中,优先用官方测试验证 WAL replay、WAL split 和 crash procedure。三条命令分别覆盖 edit 重放、死亡 server 日志拆分、procedure 编排三个观察面:
1 | |
1 | |
1 | |
在独立测试集群里观察服务恢复,应先创建实验表:
1 | |
1 | |
1 | |
1 | |
故障注入必须在可丢弃环境执行。不要在共享或生产集群上直接 kill RegionServer。实验记录只应保留 procedure 状态、Region reassignment、WAL replay 和最终读回结果,不记录无法复核的“恢复耗时”数字。
失败恢复
| 失败点 | 现象 | 排查方向 |
|---|---|---|
| WAL 不可读 | Region 打开失败或 replay 卡住 | HDFS 文件、租约、权限、WAL 损坏 |
| WAL split 积压 | ServerCrashProcedure 长时间未完成 | Master procedure、split worker、HDFS I/O |
| Region reassignment 失败 | 客户端持续 NotServingRegion 或定位失败 | AssignmentManager、hbase:meta 状态 |
| 客户端超时 | 请求重试或重新定位 | 客户端缓存、重试配置、Region 状态 |
恢复流程里最危险的写法是把组件拟人化为“自动找回”。更精确的描述是:HBase 使用持久 WAL、Region 状态机和 procedure 框架,把失败节点留下的 Region 服务权迁到存活节点,并在 Region 打开时重放 edit。
工程迁移
| 场景 | 可迁移模式 | HBase 对应物 |
|---|---|---|
| 分片服务节点死亡 | 分片重新归属 | Region reassignment |
| 内存状态丢失 | 日志重放 | WAL replay |
| 日志按分片恢复 | 分区日志拆分 | WAL split 和 recovered.edits |
| 故障恢复编排 | 持久状态机 | ServerCrashProcedure |
听到“节点挂了但内存里还有未落盘状态”时,先看可重放日志、分片归属和重放进度。只看底层文件系统副本是不够的。
常见误解
- “HDFS 会恢复 HBase 写入”不准确;HDFS 保存文件,HBase 执行 WAL replay。
- “Master 在正常读写路径上”不准确;Master 主要处理控制面和故障恢复编排,客户端正常读写直连 RegionServer。
- “WAL replay 会把所有历史 edit 再执行一遍”不准确;sequence id、flush 状态和恢复文件共同限制重放范围。
- “Region 重新打开后复制也自动完成”不准确;replication 有自己的 WAL source、peer 和 queue 语义。
练习
- 画出一个 RegionServer 同时服务三个 Region 时的 WAL split 结果。
- 给出
SKIP_WAL写入在 RegionServer 崩溃后的数据风险说明。 - 设计一份恢复观察清单,只包含 procedure、WAL、Region 状态和最终读回验证。
系列导航
- 00 导读:row key 决定数据位置
- 01 数据模型:Cell、Column Family 与版本
- 02 集群架构:HMaster、RegionServer、ZooKeeper 与 hbase:meta
- 03 Schema 与 row key 设计
- 04 写入路径:WAL、MVCC、MemStore 与 flush
- 05 读取路径:BlockCache、Bloom Filter 与 HFile block
- 06 HFile 内部结构
- 07 Compaction、TTL、版本与 Delete
- 08 Region 生命周期:split、merge 与 assignment
- 09 Client 路由与重试
- 10 行级原子性与一致性边界
- 11 RegionServer 崩溃与 WAL 恢复(本篇)
- 12 跨集群 Replication
- 13 Snapshot、Backup 与恢复
- 14 Get、Scan、Filter 与分页
- 15 BufferedMutator 与 Bulk Load
- 16 Coprocessor、Endpoint 与 Phoenix 边界
- 17 Kerberos、RPC 保护与 ACL
- 18 Metrics、hbtop、Compaction 与性能调优
- 19 HBase 3.0 与设计边界
参考资料
- Apache HBase Reference Guide:https://hbase.apache.org/docs/
- Apache HBase Architecture:https://hbase.apache.org/docs/architecture/
- Apache HBase RegionServer Architecture:https://hbase.apache.org/docs/architecture/regionserver/
- Apache HBase Developer API:https://hbase.apache.org/2.6/devapidocs/
- Apache HBase 源码:https://github.com/apache/hbase
- Apache Hadoop 3.4.3 HDFS 文档:https://hadoop.apache.org/docs/r3.4.3/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html
