Hive带distinct子查询调用自定义Lookup UDF不生效问题排查
问题根因
这个问题是UDF对Hive分布式执行上下文的兼容性问题导致的,核心原因如下:
- 你写的Lookup UDF依赖
SessionState.get()获取当前用户信息提取应用名,但SessionState是仅存在于Hive客户端会话的上下文对象,不会随UDF分发到集群节点。 - 当子查询加了
distinct关键字后,Hive会生成包含Reduce阶段的分布式执行计划做去重,UDF会被序列化分发到YARN集群的Reduce任务进程执行,该进程不存在Hive SessionState上下文,调用SessionState.get()返回null,导致loggedInApplication为空,触发initMap方法中抛出的空指针异常。 - 无
distinct时查询不需要Reduce阶段,小表场景下Hive会默认启用本地模式在客户端进程执行,SessionState上下文正常存在,因此可以正常运行。
修复方案
快速修复(最稳妥)
不要从SessionState提取应用名,直接将应用名作为UDF的入参传入,修改后调用逻辑如下:
select lookup(city, state, tax,'addresslookup', '你的应用名') from (select distinct city, state, tax from open_glory.addylookup) a
对应的UDF代码中删除setLoggedInUser相关逻辑,直接从入参取应用名拼接文件路径即可。
适配分布式上下文的修复
如果不想修改UDF调用逻辑,可以通过Hadoop配置传递应用名:
- 提交查询前先执行set命令配置应用名:
set lookup.application.name=你的应用名;
- 重写GenericUDF的
configure方法获取配置,替换原有的setLoggedInUser逻辑:
private String loggedInApplication; @Override public void configure(MapredContext context) { super.configure(context); loggedInApplication = context.getJobConf().get("lookup.application.name"); }
删除原initialize方法中调用setLoggedInUser的代码即可。
额外优化建议
现有UDF每次调用evaluate都会触发initMap判断文件是否加载,虽然有缓存逻辑,还是建议:
- 加载lookup文件时改用Hive分布式缓存提前分发文件到节点,避免每个Task都重复读HDFS
initMap方法先判断fileMap中是否已存在对应文件的缓存,存在则直接返回跳过加载逻辑,减少不必要的判断开销
内容的提问来源于stack exchange,提问作者Haresh Amin
相关产品推荐
相关产品推荐

