大数据第四篇:事故复盘:移走 HBase 里 27 个 hadoop-2.10.2 jar 后,HMaster 半小时后“自杀“了【jar 事故复盘】

一篇用完整报错证据链讲透的复盘:HBase(hadoop2 发行版)自带的 27 个 hadoop-2.10.2 jar 不是"污染",删掉后 MR 作业确实跑通了,但数小时后 HMaster 在 flush 时抛 NoSuchMethodError 集体自杀;本文给出日志取证方法、安全回滚步骤和 6 条新增运维坑。

目录

  • 一、事故经过:一次"成功"的优化
  • 二、根因分析:一个方法签名引发的血案
  • 三、为什么当时没发现?三个误判点
  • 四、日志取证:怎么证明是 jar 的锅
  • 五、安全回滚:从崩溃到恢复的完整步骤
  • 六、★正确姿势:服务端保留、客户端绕开
  • 七、新增的 6 条运维坑
  • 八、写在最后

一、事故经过:一次"成功"的优化

背景:HBase 2.5.16(hadoop2 发行版 )跑在 Hadoop 3.3.6 之上。lib 目录里躺着 27 个 hadoop-*2.10.2*.jar,看着就和 3.3.6 的集群"不搭"。做千万行 ImportTsv 导入时,MR 作业起动即报 ClassCastException ...GetFileInfoRequestProto------于是做出了一个看起来很合理的决定:

bash 复制代码
# 错误操作(请勿模仿):把 27 个 2.10.2 jar 移出 lib/
mkdir /home/hduser/hbase/lib/unused-hadoop2102
mv /home/hduser/hbase/lib/hadoop-*2.10.2*.jar /home/hduser/hbase/lib/unused-hadoop2102/

然后:重启 HBase,一切正常;MR 作业跑通了;导入完成了。看起来优化成功------直到当天 HMaster 在一次例行 flush 时抛出 NoSuchMethodError,直接 ABORTING,集群不可用。

关键报错(铁证日志):

复制代码
ERROR [master/store-Flusher] regionserver.HStore: Failed flushing store ...
DroppedSnapshotException: ...
Caused by: java.lang.NoSuchMethodError:
  org.apache.hadoop.hdfs.DFSInputStream.getReadStatistics()
  Lorg/apache/hadoop/hdfs/DFSInputStream$ReadStatistics;
... ABORTING master hadoop1,16000
... KeeperErrorCode = NoNode for /hbase/master

二、根因分析:一个方法签名引发的血案

HBase 2.5.16 的 hadoop2 发行版是针对 Hadoop 2.10.2 编译的 。HdfsDataInputStream.getReadStatistics() 这个方法:

环境下 返回类型
Hadoop 2.10.2(HBase 期望) DFSInputStream$ReadStatistics(内部类路径)
Hadoop 3.3.6(实际加载) 独立的 ReadStatistics 类

字节码里方法签名是硬编码的。移走 2.10.2 jar 后,HBase 加载到 3.3.6 的实现------方法名相同、签名不同,JVM 直接抛 NoSuchMethodError。这条代码路径在 WAL/flush 时才走到 ,所以启动、读写、MR 全都正常,只在 flush 触发时爆炸,且一旦爆炸 HMaster 主动 ABORTING 自杀,ZK 上的 master 节点随即消失。

一句话结论:这 27 个 jar 是 hadoop2 发行版的配套依赖,不是"污染";服务端必须和它一起用,MR 侧只能绕开它。

三、为什么当时没发现?三个误判点

  1. 验证只盯了 MR 侧:改动的动机是修 MR 报错,验证也只验证了 MR 跑通,没盯服务端日志------但这次改动同时影响"服务端 + MR 客户端"两个消费者。
  2. 故障是延迟暴露的:flush 不是每秒发生,从重启到爆炸隔了数小时到数十分钟不等。"改完重启、跑了一圈没事"≠安全。
  3. 被"日志干净"麻痹:翻 RegionServer 日志没看到报错------但 flush 路径在 Master 侧,而且没触发 flush 时本来就不会有报错。

方法论:一次改动影响多个消费者时,必须两端同时验证,且覆盖延迟触发路径(flush、compaction、重启)。

四、日志取证:怎么证明是 jar 的锅

4.1 锚定行首时间戳过滤(别用 awk 范围比较)

异常堆栈的续行没有时间戳 ,awk '$0 >= "时间戳"' 会把历史堆栈的续行也收进来,得出完全相反的结论。正确姿势:

bash 复制代码
grep -E '^2026-10-10T(10:5[5-9]|11):' \
  /home/hduser/hbase/logs/hbase-hduser-master-hadoop1.log \
  | grep -cE 'NoSuchMethodError|ABORTING'
# 只统计以日期开头的行,计数归零才算真没有

4.2 确认服务端实际加载的 jar

hbase classpath 不是服务端判据! 它是客户端视角(走 shaded-client,不含 lib/*.jar)。正确判据是看 HMaster 进程实际打开了什么:

bash 复制代码
lsof -p <HMaster的PID> | grep hadoop-common
# 应该看到 hadoop-common-2.10.2.jar ------ 有它,flush 才不会炸

五、安全回滚:从崩溃到恢复的完整步骤

bash 复制代码
# 1. jar 放回(27 个,放完核对数量)
mv /home/hduser/hbase/lib/unused-hadoop2102/*.jar /home/hduser/hbase/lib/
ls /home/hduser/hbase/lib/hadoop-*2.10.2*.jar | wc -l   # 应输出 27

# 2. 停 HBase(注意:stop-hbase.sh 先停 ZK,Master 可能卡在重连循环,
#    确认数据都在 HDFS 后可以 kill -9 兜底)
su - hduser
stop-hbase.sh
# 3. 从节点 RegionServer 可能残留(no pid file 被跳过),逐台补停
hbase-daemon.sh stop regionserver

# 4. 重启并盯 flush
start-hbase.sh
grep -c "Master has completed initialization" \
  /home/hduser/hbase/logs/hbase-hduser-master-hadoop1.log   # =1 即就绪

回滚后验证(三项全过) :手动触发 flush 恢复(日志出现 Finished flush of dataSize ~143.18 KB,正是先前触发自杀的那一步);重启后 NoSuchMethodError 计数为 0;Web UI 显示 1 active master + 2 servers,iot 表 1000 万行完好(HDFS 1.7G)。数据零损失。

六、★正确姿势:服务端保留、客户端绕开

问题 结论
能否移走 27 个 jar? 不能,服务端必须保留
MR 侧 ClassCastException 怎么解? 手工拼 classpath 排除 2.10.2 直接 java 启动工具类,零文件改动
长期正解? 换官方 hbase-2.5.16-hadoop3-bin 发行版(需停机窗口重建/迁移数据)

七、新增的 6 条运维坑

  1. stop-hbase.sh 先停 ZK ,Master 卡在 ZK 重连循环 → 先 hbase-daemon.sh stop master,或确认数据在 HDFS 后 kill -9。
  2. pgrep -f HMaster 会匹配到命令本身 → 判存活用端口(16000/16020/2181)或 ps -eo args | grep '[H]Master'。
  3. stop 后 RegionServer 残留(no pid file 被脚本跳过)→ 逐台补停。
  4. 日志过滤误报 → 锚定行首时间戳(见 4.1)。
  5. 共用依赖改动只验一端 → 双端验证 + 延迟触发路径。
  6. Web UI 看不到数据(16010/16030/9870 都一样)→ 查数据走 Phoenix SQL 或图形客户端(系列五、六)。

八、写在最后

这次事故最值得记住的不是那个 NoSuchMethodError,而是**"验证通过"的假象是怎么造成的**:验证范围 ≠ 影响范围,观察窗口 < 触发延迟。以后再想清理任何"看着不顺眼"的依赖 jar 前,先问一句:这个 jar 的字节码,到底在服务端哪些路径上被调用?

下一篇预告:《系列五:Phoenix 5.3.2 集成------让 HBase 说 SQL,千万行 COUNT 从 88 秒到 25 秒》。hbase shell 的 scan 终究难写,Phoenix 用协处理器把查询下推到数据所在节点,附 SALT_BUCKETS 与 EXPLAIN 实战。

相关推荐
wuminyu2 小时前
JVM规范学习总结-JVM规范基本概念
java·linux·c语言·jvm·c++
学到老活到老17 小时前
方法执行完了,对象为什么还占着内存?
java·jvm
Francek Chen1 天前
【大数据处理与分析】数据仓库Hive:08 Impala
数据仓库·hive·hadoop·数据分析·impala
骑着蜗牛撵大象3271 天前
四种库收进一个SQL编辑器(下):跨库取数、方言对齐与四库巡检的落地拆解
jvm·数据库·sql·编辑器·数据迁移·跨库查询
zoyation1 天前
HBase 2.5.16 单机部署
大数据·数据库·hbase
学习论之费曼学习法1 天前
无人值守AI内容矩阵实战(7):Playwright 发布器避坑——编辑器竞态、索引延迟与风控
python·线性代数·矩阵·编辑器·浏览器自动化·playwright·踩坑记录
隔窗听雨眠1 天前
开源为骨自研为魂:存储统一管理平台的混合架构实践
性能优化·实战教程·原理解析·踩坑记录
ha_lydms2 天前
Window Functions(窗口函数)
大数据·数据仓库·hadoop·spark·数据处理·dataworks·maxcompute
需要8262 天前
容器化 Java:镜像瘦身与 JVM 容器感知(cgroup)
java·开发语言·jvm