Hive UDF/GenericUDF开发

本文详细介绍Hive中用户自定义函数(UDF)的开发方法,包括针对简单数据类型的UDF和复杂数据类型的GenericUDF。通过示例展示了如何使用Java实现MD5哈希和字符串切分功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关键字:Hive udf、UDF、GenericUDF

Hive中,除了提供丰富的内置函数(见[一起学Hive]之二–Hive函数大全-完整版)之外,还允许用户使用Java开发自定义的UDF函数。

开发自定义UDF函数有两种方式,一个是继承org.apache.hadoop.hive.ql.exec.UDF,另一个是继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF;

如果是针对简单的数据类型(比如String、Integer等)可以使用UDF,如果是针对复杂的数据类型(比如Array、Map、Struct等),可以使用GenericUDF,另外,GenericUDF还可以在函数开始之前和结束之后做一些初始化和关闭的处理操作。

UDF

使用UDF非常简单,只需要继承org.apache.hadoop.hive.ql.exec.UDF,并定义

public Object evaluate(Object args) {} 方法即可。

比如,下面的UDF函数实现了对一个String类型的字符串取HashMD5:

package com.lxw1234.hive.udf;
 
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.MD5Hash;
import org.apache.hadoop.hive.ql.exec.UDF;
 
public class HashMd5 extends UDF {
public String evaluate(String cookie) {
return MD5Hash.getMD5AsHex(Bytes.toBytes(cookie));
}
}

将上面的HashMd5类打成jar包,udf.jar

使用时候,在Hive命令行执行:

add jar file:///tmp/udf.jar;
CREATE temporary function str_md5 as 'com.lxw1234.hive.udf.HashMd5';
select str_md5(‘lxw1234.com’) from dual;

GenericUDF

继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF之后,需要重写几个重要的方法:

public void configure(MapredContext context) {}

//可选,该方法中可以通过context.getJobConf()获取job执行时候的Configuration;

//可以通过Configuration传递参数值

public ObjectInspector initialize(ObjectInspector[] arguments)

//必选,该方法用于函数初始化操作,并定义函数的返回值类型;

//比如,在该方法中可以初始化对象实例,初始化数据库链接,初始化读取文件等;

public Object evaluate(DeferredObject[] args){}

//必选,函数处理的核心方法,用途和UDF中的evaluate一样;

public String getDisplayString(String[] children)

//必选,显示函数的帮助信息

public void close(){}

//可选,map完成后,执行关闭操作

 

下面的程序将一个以逗号分隔的字符串,切分成List,并返回:

package com.lxw1234.hive.udf;
 
import java.util.ArrayList;
import java.util.Date;
 
import org.apache.hadoop.hive.ql.exec.MapredContext;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
 
/**
* http://lxw1234.com
* lxw的大数据田地
* @author lxw1234
* 该函数用于将字符串切分成List,并返回
*/
public class Lxw1234GenericUDF extends GenericUDF {
private static int mapTasks = 0;
private static String init = "";
private transient ArrayList ret = new ArrayList();
@Override
public void configure(MapredContext context) {
System.out.println(new Date() + "######## configure");
if(null != context) {
//从jobConf中获取map数
mapTasks = context.getJobConf().getNumMapTasks();
}
System.out.println(new Date() + "######## mapTasks [" + mapTasks + "] ..");
}
@Override
public ObjectInspector initialize(ObjectInspector[] arguments)
throws UDFArgumentException {
System.out.println(new Date() + "######## initialize");
//初始化文件系统,可以在这里初始化读取文件等
init = "init";
//定义函数的返回类型为java的List
ObjectInspector returnOI = PrimitiveObjectInspectorFactory
.getPrimitiveJavaObjectInspector(PrimitiveObjectInspector.PrimitiveCategory.STRING);
return ObjectInspectorFactory.getStandardListObjectInspector(returnOI);
}
 
@Override
public Object evaluate(DeferredObject[] args) throws HiveException {
ret.clear();
if(args.length < 1) return ret;
//获取第一个参数
String str = args[0].get().toString();
String[] s = str.split(",",-1);
for(String word : s) {
ret.add(word);
}
return ret;
}
 
@Override
public String getDisplayString(String[] children) {
return "Usage: Lxw1234GenericUDF(String str)";
}
 
}

其中,在configure方法中,获取了本次任务的Map Task数目;

在initialize方法中,初始化了一个变量init,并定义了返回类型为java的List类型;

getDisplayString方法中显示函数的用法;

evaluate是核心的逻辑处理;

 

需要特别注意的是,configure方法,“This is only called in runtime of MapRedTask”,该方法只有在运行map task时候才被执行。它和initialize用法不一样,如果在initialize时候去使用MapredContext,则会报Null,因为此时MapredContext还是Null。

 

上面的函数执行后,在MapReduce的日志中打印出了以下内容:

hive udf

即在MapReduce阶段,GenericUDF几个方法的执行顺序为:

configure–>initialize–>evaluate–>close

 

Hive相关文章(持续更新)

一起学Hive系列

—-Hive概述,Hive是什么

—-Hive函数大全-完整版

—-Hive中的数据库(Database)和表(Table)

—-Hive的安装配置

—-Hive的视图和分区

—-Hive的动态分区

—-向Hive表中加载数据

—-使用Hive命令行

—-Hive的查询语句SELECT

—-Hive中Join的原理和机制

—-Hive中Join的类型和用法

—-Hive SQL的优化

—-Hive整合HBase,操作HBase表

—-Hive的元数据表结构详解

—-分析Hive表和分区的统计信息(Statistics)

—-Hive的WEB页面接口-HWI

—-从Hive表中进行数据抽样-Sampling

Hive分析函数系列

Hive索引

hive优化之——控制hive任务中的map数和reduce数

 

如果觉得本博客对您有帮助,请 赞助作者 。

转载请注明:lxw的大数据田地 » [一起学Hive]之十八-Hive UDF开发

<think>嗯,用户问的是HiveUDFGenericUDF的区别。我需要先回忆一下Hive UDF的基本知识。HiveUDF允许用户自定义函数来处理数据,但UDF有不同的类型,比如普通的UDFGenericUDF。首先,我应该明确它们的定义和用途。 普通UDF应该是指继承自UDF类的那些,它们主要用于处理简单的数据类型,比如字符串、整数等。而GenericUDF可能更复杂,处理更复杂的数据结构,比如Map、Array或者Struct。这可能涉及到Hive中的复杂数据类型,这时候可能需要GenericUDF。 然后,我需要考虑它们的实现方式。普通UDF可能通过evaluate方法来实现,而GenericUDF可能需要实现initialize和evaluate方法,因为GenericUDF更灵活,能处理动态的参数和类型。这也意味着GenericUDF在类型处理上更安全,支持更多的Hive特性,比如延迟对象展开(DeferredObject),这样可以在运行时解析参数,避免不必要的计算。 另外,性能方面可能也有差异。普通UDF因为处理简单类型,可能更高效,而GenericUDF在处理复杂类型时虽然灵活,但可能有额外的开销。不过这点需要确认,比如Hive的文档是否提到性能差异。 还需要举例子来说明两者的不同。比如,普通UDF可以是一个简单的字符串处理函数,而GenericUDF可能处理数组的某个元素,或者更复杂的逻辑。比如,Hive内置的nvl函数可能用GenericUDF实现,因为它需要处理不同的输入类型。 另外,用户可能需要知道何时选择使用哪一种。比如,处理基本类型时用UDF,处理复杂类型或者需要更灵活的类型处理时用GenericUDF。同时,GenericUDF提供了更好的类型检查和错误处理,这在开发复杂函数时很重要。 需要确保回答结构清晰,分点说明区别,包括实现方式、处理的数据类型、灵活性、性能等方面。同时给出示例代码片段,帮助用户理解两者在代码结构上的不同。 最后,检查是否有遗漏的关键点,比如Hive版本的影响,或者是否有其他类型的UDF,比如UDAF或UDTF,但用户只问了UDFGenericUDF,所以不需要展开。确认使用中文,并且数学表达式如果有的话用正确的格式,但这个问题可能不涉及数学公式,所以可能不需要。但用户给的系统指令里有提到数学表达式,所以如果有需要的地方要用$符号,不过这个问题可能用不上。</think>在 Hive 中,**UDF(User-Defined Function)****GenericUDF** 是两种自定义函数的实现方式,核心区别如下: --- ### 一、实现方式与灵活性 1. **UDF** - 继承 `org.apache.hadoop.hive.ql.exec.UDF` 类。 - 通过重载 `evaluate()` 方法实现逻辑。 - **局限性**: - 仅支持基本数据类型(如 `String`, `Int`, `Double` 等)。 - 参数和返回值类型需在编译时确定,无法动态推导复杂类型(如 `Map`, `Array`)。 ```java public class SimpleUDF extends UDF { public String evaluate(String input) { return input.toUpperCase(); } } ``` 2. **GenericUDF** - 继承 `org.apache.hadoop.hive.ql.udf.generic.GenericUDF` 类。 - 需实现 `initialize()`, `evaluate()`, `getDisplayString()` 方法。 - **优势**: - 支持复杂数据类型(如 `Struct`, `List`, `Map`)。 - 动态类型推导:通过 `ObjectInspector` 在运行时解析输入/输出类型。 - 可处理可变参数和延迟计算(`DeferredObject`)。 ```java public class ComplexGenericUDF extends GenericUDF { @Override public ObjectInspector initialize(ObjectInspector[] arguments) { // 类型检查和初始化逻辑 } @Override public Object evaluate(DeferredObject[] arguments) { // 核心计算逻辑 } } ``` --- ### 二、类型处理机制 - **UDF**:依赖 Java 原生类型(如 `String`, `Integer`),需手动处理 Hive 类型与 Java 类型的转换。 - **GenericUDF**:通过 `ObjectInspector` 直接操作 Hive 内部数据结构,避免类型转换开销,且支持复杂类型。 --- ### 三、典型场景 1. **优先使用 UDF** - 简单标量计算(如字符串处理、数值运算)。 - 示例:字符串转大写、日期格式化。 2. **必须使用 GenericUDF** - 处理 `Array/Map/Struct` 等嵌套结构。 - 需要动态类型推导(如函数参数类型不固定)。 - 示例:提取 `JSON` 字段、复杂条件分支逻辑。 --- ### 四、性能差异 - **UDF**:直接操作 Java 类型,性能较高,但仅适用于简单场景。 - **GenericUDF**:因涉及 `ObjectInspector` 和 Hive 内部对象,可能略慢,但灵活性更强。 --- ### 五、代码复杂度 - **UDF**:代码简单,适合快速开发。 - **GenericUDF**:需理解 Hive 类型系统,开发成本较高。 --- ### 总结对比表 | **特性** | **UDF** | **GenericUDF** | |-------------------|------------------------------------|-------------------------------------| | 继承类 | `UDF` | `GenericUDF` | | 支持数据类型 | 基本类型(String, Int 等) | 基本类型 + 复杂类型(Array, Map 等)| | 类型推导 | 编译时确定 | 运行时动态推导 | | 灵活性 | 低 | 高(支持可变参数、延迟计算) | | 开发难度 | 简单 | 复杂 | | 典型场景 | 简单标量计算 | 复杂逻辑和嵌套类型处理 | --- 通过合理选择 UDFGenericUDF,可以更高效地实现 Hive 中自定义函数的业务需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值