Java中的Hive UDF在创建表时失败

Posted

技术标签:

【中文标题】Java中的Hive UDF在创建表时失败【英文标题】:Hive UDF in Java fails when creating a table 【发布时间】:2020-07-31 12:45:16 【问题描述】:

这两个查询有什么区别:

SELECT my_fun(col_name) FROM my_table;

CREATE TABLE new_table AS SELECT my_fun(col_name) FROM my_table;

其中 my_fun 是一个 java UDF。

我在问,因为当我创建新表(第二个查询)时,我收到一个 java 错误。

Failure while running task:java.lang.RuntimeException: java.lang.RuntimeException: Map operator initialization failed
...
Caused by: org.apache.hadoop.hive.ql.exec.UDFArgumentException: Unable to instantiate UDF implementation class com.company_name.examples.ExampleUDF: java.lang.NullPointerException

我发现错误的来源是我的java文件中的一行:

encoded = Files.readAllBytes(Paths.get(configPath));

但问题是为什么它在未创建表时有效而在创建表时失败?

【问题讨论】:

你的表有多少行?如果它真的很小,第一个查询可能在本地执行,而第二个查询只在 Hadoop 集群上执行。还有,configPath是本地路径还是HDFS路径? create-table-as-select(第二个示例)是 CTAS。它对可能导致问题的表有一些限制。你读过CTAS in the Hive DDL manual 的部分吗?它列出了两个限制: 1:目标表不能是外部表。 2:目标表不能是列表分桶表。这些都适用吗? @serge_k 似乎与表大小无关,即大小表都失败。我在本地文件系统和 hdfs 上测试了 configPath,它也失败了。但这可能是一个好点,因为此路径被设置为环境变量。如果它是硬编码的,它可以工作。我必须检查一下。 @zac 不,在这种情况下不适用。 【参考方案1】:

问题可能与您读取文件的方式有关。尝试将文件路径作为UDF中的第二个参数传递,然后读取如下

private BufferedReader getReaderFor(String filePath) throws HiveException 
    try 
        Path fullFilePath = FileSystems.getDefault().getPath(filePath);
        Path fileName = fullFilePath.getFileName();
        if (Files.exists(fileName)) 
            return Files.newBufferedReader(fileName, Charset.defaultCharset());
        
        else
        if (Files.exists(fullFilePath)) 
            return Files.newBufferedReader(fullFilePath, Charset.defaultCharset());
        
        else 
            throw new HiveException("Could not find \"" + fileName + "\" or \"" + fullFilePath + "\" in inersect_file() UDF.");
        
    
    catch(IOException exception) 
        throw new HiveException(exception);
    


private void loadFromFile(String filePath) throws HiveException 
    set = new HashSet<String>();

    try (BufferedReader reader = getReaderFor(filePath)) 
        String line;
        while((line = reader.readLine()) != null) 
            set.add(line);
        
     catch (IOException e) 
        throw new HiveException(e);
    

可以找到使用文件阅读器的不同通用 UDF 的完整代码here

【讨论】:

【参考方案2】:

我认为有几点不清楚,所以这个答案是基于假设的。

首先,重要的是要了解 hive 当前优化了几个简单查询,并且根据您的数据大小,为您工作的查询SELECT my_fun(col_name) FROM my_table; 很可能从您正在执行的客户端本地运行这项工作,这就是为什么您 UDF 可以在本地访问您的配置文件的原因,这种“执行模式”是因为您的数据大小。 CTAS 触发独立于输入数据的作业,该作业在集群中分布式运行,每个工作人员无法访问您的配置文件。

看起来您正在尝试从本地文件系统读取配置文件,而不是从 HDSFS Files.readAllBytes(Paths.get(configPath)),这意味着您的配置必须在所有工作节点中复制或预先添加到分布式缓存(您可以使用 add file from this, doc here。您可以找到关于从 UDF 访问分布式缓存中的文件的其他问题 here。

另一个问题是,您通过环境变量传递配置文件的位置,该环境变量不会作为 hive 作业的一部分传播到工作程序节点。您应该将此配置作为配置单元配置传递,假设您正在扩展 GenericUDF,则有一个从 UDF here 访问配置单元配置的答案。

【讨论】:

以上是关于Java中的Hive UDF在创建表时失败的主要内容,如果未能解决你的问题,请参考以下文章

hive的(ql)hql使用和基于UDF的用法;以及java对hive的远程访问

Hive常用命令

Hive/Athena:创建表时忽略字段

当您在 Hive 中使用 S3 位置创建外部表时,数据何时传输?

hive函数之~reflect函数

Hive- 表