hive 大小表mapjoin 遇到udf失效问题
执行结果有三个重要信息:
- /tmp/liangxin/liangxin_20201026173636_ddccc70a-1019-49d4-9cc8-6b072023187a.log
- Stage-4
- /tmp/liangxin/hive.log
问题分析:
- 打开第一个文件发现udf函数没找到,对比执行sql,on dwc_mart.transtring(a.altitem)=dwc_mart.transtring(b.pa_code) 使用了udf
- explain 执行语句,找到stage4,发现启动了本地模式,本地模式下进行了mapjoin操作
- 打开第三个log,发现 ql.Driver报错
原理分析
下面是mapjoin执行进程图。
左边是小表的执行过程,udf包放在了hdfs上,在本地模式下,不会去hdfs上找udf的jar包,所以启动local task jvm时找不到对应的jar包报错。
- 在hive客户端提交上面Sql,在客户端本地启动一个DriverJVM进程,
- Driver 进程解析脚本,语法分析,语意分析,读取元数据,生成逻辑执行计划,逻辑优化,最终生成一个物理执行计划。
- 接下来执行物理执行计划。对于这个SQL来说,流程如下。
- 首先从HDFS下载UDF jar包到 Driver进程中,放在classpath 中。这一步可以可以从日志中看到,创建临时自定义函数。
- 因为右侧表是小表,所以生成的物理执行计划是mapjoin ,那么就在客户端本地机器启动一个LocalTask JVM进程,(这一步可以从日志中看到)用来从HDFS读取小表数据到内存中,在内存中转换为HashTable,根据语句,在这里执行自定义函数。
- 当Local Task 执行完毕后,Driver 进程负责将classpath 中jar 包、物理执行计划、小表生成的HashTable 提交Yarn 上去执行。
- 根据物理执行计划,Yarn上执行的 ApplicationMaster 分配多个Map 节点从HDFS上读取左侧大表数据,同时会将小表的HashTable分发到各个Map节点中,每个节点分配一个小表全量放在内存里。
- 然后每个Map节点处理大表的一部分数据,同时与小表进行关联,这一步就是mapjoin,关联的结果输出写入HDFS。
根据日志,显然是在Local Task这一步出错了,报的错是ClassNotFound,为什么找不到UDF的类呢?所以猜测jar包的加载是在Driver JVM中做的,Local Task中并没有加载jar包,所以找不到类。
解决策略
- 避免mapjoin使用udf函数。在join之前处理好对应列。
- 关闭mapjoin,设置set hive.auto.convert.join=false;
- 在单机模式下,本地加载udf包(待验证)