大数据第四篇:事故复盘:移走 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 实战。

相关推荐
寂寞的船3 天前
【Unity知识速递】第10期 Unity GC 零分配实战:从 UnityEngine.Pool 到手动 GC 控制的全链路优化
jvm·unity·c#·游戏引擎·lucene
金銀銅鐵5 天前
同一个 Hello World,Kotlin 和 Java 在字节码层面差在哪
jvm·后端·kotlin
yeccheng6 天前
MySQL 索引超详解:从底层 B+ 树到面试高频考点
java·开发语言·jvm·mysql·面试
Francek Chen7 天前
【大数据处理与分析】数据仓库Hive:09 Hive 编程实践
大数据·数据仓库·hive·hadoop·sql
On The Road :8 天前
深入理解 JVM 垃圾回收:GC 是什么、如何判定垃圾、四大回收算法全解析
jvm·算法
Yves..8 天前
JVM 学习笔记:内存区域、类加载机制与垃圾回收
jvm
akinggw9 天前
libagentcore - 一个基于c++的AI多agent系统升级到2.0版本
开发语言·jvm·c++
yh弓长9 天前
JVM 核心机制
jvm
茶海棠10 天前
Hadoop+zookeeper+HBase+Kafka集群搭建全流程
hadoop·zookeeper·kafka·hbase
tg_xianheyun10 天前
腾讯云国际账号注册代充值适合哪些海外业务场景?
jvm