记一次Hive和Hadoop版本兼容问题导致执行Insert操作的错误跟踪
1. 问题描述
今天在搭建了Hadoop+Hive环境之后,在尝试使用Hive进行数据插入操作时,发现在插入数据时,Hive执行报错,无法完成数据插入。
在进一步描述问题之前,先介绍下运行环境,因为确实跟版本的兼容性有点关系:
在执行这个简单的 Insert 操作后,等待了一段时间,最终报错,错误如下图:
截图是 DataGrip 环境中执行的一个简单的 Insert 操作。
2. 跟踪定位
上面的(Hive 返回的)错误提示很简单,但是 org.apache.hadoop.hive.ql.exec.mr.MapRedTask 提示我们在执行 MapRed 任务时候报错的,因此我们需要到 YARN 的控制台上寻找错误日志。如下图:
在这张 Job 列表的截图中,第二条是我执行 HQL 语句生成的 MapReduce Job,该 Job 状态为 FAILED,进一步点击 Job ID 链接,进入 Job 详情页面,如下图:
从 MapReduce Job 详情页可以看到 Diagnostics 那一行显示了哪个 Task 执行失败,我们继续点击 Failed Task ID,进入 Task 详情页面,如下图:
在 Task 详情页面,我们看到 Attempt 记录行的 Note 这一列,显示了简要的错误提示,这里为 protobuf 库中的 com.google.protobuf.Internal.checkNotNull 方法的问题,我们需要进一步确认,继续点击 Logs 列的 logs 链接,进入 Task 日志页面,如下图:
在这个 Task 日志页面,列出了各种类型的部分关联日志,如:directory.info,prelaunch.err,stderr,syslog 等等日志类型,我们找到 syslog 日志类型部分,即上图红框部分,然后需要点击那个 Click here 的 here 文字链接,进入 syslog 日志详情页面,如下图:
这里的错误信息提示为:
Error running local (uberized) 'child' : java.lang.NoSuchMethodError: com.google.protobuf.Internal.checkNotNull
这是找不到 protobuf 库的 Internal 类中 checkNotNull 方法,进一步看异常调用栈,可以看到是在 Map 阶段时(org.apache.hadoop.mapred.MapRunner.run(MapRunner.java:61)),准备 write orc 格式文件过程中,在解析 Map(xml格式)的 child 节点时,调用 protobuf 库的 Internal.checkNotNull 方式时报 NoSuchMethodError 错误。
上面创建的表结构指定了存储格式为 orc,所以这里是 Hive 的 orc-core 库在 write 的时候,调用了 protobuf 的 Internal.checkNotNull 方法。
那就先看一下 YARN 的依赖库中的 protobuf 包版本,如下路径:
$HADOOP_HOME/share/hadoop/yarn/lib/protobuf-java-2.5.0.jar
进一步找到 protobuf-java-2.5.0 版本的源码,查了一下 Internal 方法,确实没有 checkNotNull 方法,如下图:
也就是这个 Hadoop 3.4.0 版本下的 Yarn 依赖库的 protobuf 包版本与 Hive 4.0.0 版本的 orc 需要访问的 protobuf 包版本不匹配。
3. 我的解决方法
我目前的解决方法简单粗暴了些,就是将 Hive 中依赖的 protobuf 版本(protobuf-java-3.24.4) 覆盖到 YARN 的依赖库中,下图为 Hive 下的 protobuf 版本:
替换好 protobuf-java-3.24.4 版本后,重启 YARN 服务,再次执行 HQL 语句,成功完成数据插入。如下图:
进一步到 HDFS 上查看确认,也已经写入了数据文件。