在Hive中通过Java和Python实现UDF进行对比

本文详细介绍了如何在Hive中使用用户自定义函数(UDF)来处理大数据。首先,通过Java实现UDF,讲解了创建一个查询特定关键字的UDF过程,包括编写Java类、打包成jar文件并在Hive中注册和使用。接着,展示了使用Python实现UDF,说明Python脚本的读写要求及在HiveQL中的调用方式。最后,对比了Java和Python实现UDF的异同,强调了性能和使用便利性的差异。

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

当业务数据量过大,不能在关系型数据库中统计时,可以通过 Sqoop 导入到 HDFS 上进行统计,类似日志数据一样。导入到 HDFS 上数据,每一条记录通过 \t 或 Ctrl+A 等分割每一个字断,通过 \n 分割每一条记录。然后,通过建立 Hive 表指向 HDFS 对应的路径数据,对 HDFS 上的数据添加 Schema 定义,可以基于 Hive 中 SQL 的语法进行查询统计。

尽管 Hive 提供了丰富的函数,有时仍然不能满足个性化的查询和统计要求。这时,需要用户实现 UDF

概述

UDF,即 User-Defined Function,用户通过自定义对数据的处理过程,以函数的形式在 HiveQL 中使用。

有 2 种方式在 HiveQL 中实现 UDF。 第一种方式 ,即通过 Java 或其他通过 JVM 实现的语言(Jython、JRuby、Clojure、Groovy),实现UDF; 第二种方式 ,通过 TRANSFORM...AS 语法,使用可执行的脚步语言实现 UDF。 接下来,将分别介绍通过 Java 和 Python 实现 Hive 中的 UDF,最后,比较二者在使用过程中的异同。

假设有一张 Hive 表,描述订单基本信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE TABLE IF NOT EXISTS order_base(
id                 INT         COMMENT  '自增ID',      
order_sn           STRING      COMMENT  '订单编号',
user_id            INT         COMMENT  '用户ID',
shop_id            INT         COMMENT  '店铺ID',
add_time           INT         COMMENT  '下单时间',
pay_time           INT         COMMENT  '付款时间',
delivery_name      STRING      COMMENT  '收件人姓名',
delivery_address   STRING      COMMENT  '收件人地址',
delivery_phone     STRING      COMMENT  '收件人电话'
)COMMENT '订单基本信息表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS TEXTFILE

现在需要查询收件人地址中出现关键字 大学 的记录,找出对应的 (order_sn, delivery_address),通过 UDF 实现。

Java实现UDF

通过 Java 实现 UDF 时,Java 中的类必须继承类 the org.apache.hadoop.hive.ql.exec.UDF。 并且,UDF 的实现过程必须写在名称为 evaluate 的函数中。由于 evaluate 并未在 UDF 父类中具体说明,因此,用户实现的UDF中,函数 evaluate 的返回类型和参数可以自己指定。当用户实现的 UDF 在 Hive 中执行时,Hive 将会执行函数 evaluate 中的内容。

这里,查询出现指定关键字记录的UDF的Java实现过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.whlminds.hive.udf;

import org.apache.hadoop.hive.ql.exec.UDF;

/**
 * @date Oct 7, 2022
 *
 * @author 
 *
 * @Description:  查询出现指定关键字的记录,出现则返回 1,否则返回 0
 *
 */
public class FindSpecifiedWords extends UDF {

	public int evaluate(String targetWords, String rawWords) {
		int found = 0;
		if (rawWords.contains(targetWords)) {
			found = 1;
		}
		return found;
	}
}

由于 org.apache.hadoop.hive.ql.exec.UDF 并非 Java 自带库函数,因此,这里需要引用包含 Hive API的 jar 包,这里是 hive-0.4.1.jar

用 Java 实现的 UDF 完成了,打包为 hive-udf-java.jar,现在,需要在 HiveQL 中调用。

在 HiveQL 中使用 UDF 前,首先需要添加 jar 包至 Hive 中,对自定义函数 com.whlminds.hive.udf.FindSpecifiedWords 进行注册,可以被 Hive 中脚本使用:

1
hive> ADD JAR /mnt/whlminds/hive-udf-java.jar;

这里,对 com.whlminds.hive.udf.FindSpecifiedWords 起别名,方便调用:

1
CREATE TEMPORARY FUNCTION findwords AS 'com.whlminds.hive.udf.FindSpecifiedWords';

上述步骤,实现了 UDF,在Hive中注册,并起别名方便调用,接下来,就可以在 HiveQL 中使用了:

1
2
3
4
5
SELECT t1.order_sn, t1.delivery_address
FROM order_base t1
WHERE t1.pay_time >= UNIX_TIMESTAMP('2015-10-04 00:00:00')
AND t1.pay_time < UNIX_TIMESTAMP('2015-10-05 00:00:00')
AND findwords('大学', t1.delivery_address) = 1;

为查询方便,可以将 Hive 中注册,起别名,以及调用过程一起写在SQL脚本 find_specified_order.sql 中:

1
2
3
4
5
6
7
8
9
ADD JAR /mnt/whlminds/hive-udf-java.jar;

CREATE TEMPORARY FUNCTION findwords AS 'com.whlminds.hive.udf.FindSpecifiedWords';

SELECT t1.order_sn, t1.delivery_address
FROM order_base t1
WHERE t1.pay_time >= UNIX_TIMESTAMP('2015-10-04 00:00:00')
AND t1.pay_time < UNIX_TIMESTAMP('2015-10-05 00:00:00')
AND findwords('大学', t1.delivery_address) = 1;

然后,执行 SQL 脚本:

1
hive> hive -f find_specified_order.sql

Python实现UDF

通过 Python 实现 Hive 的 UDF,Python 脚本需要以特定的方式读入和输出,除了必须引用 sys 包外,无须引用其他外部包。

这里,查询出现指定关键字记录的 UDF 的 Python 实现过程 FindSpecifiedWords.py 如下:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python

import sys

for line in sys.stdin:
    order_sn, delivery_address = line.strip().split("\t")

    found = "N"
    pos = delivery_address.decode("utf8").find(u'\u5927\u5b66')
    if(pos > -1):
        found = "Y"
    print "\t".join([order_sn, delivery_address, found])

这里,Python 实现的 UDF,需要批量的读入数据,并一对一的批量输出。其中,u'\u5927\u5b66' 是 大学 的 utf8 的编码。

使用 Python 实现的 UDF 完成后,需要通过 ADD FILE 指令添加至 Hive 中进行注册,无需起别名:

1
hive > ADD FILE /mnt/whlminds/FindSpecifiedWords.py

注册完后,Python 实现的 UDF 就可以通过 TRANSFORM...AS 在 HiveQL 中使用,语法如下:

1
2
3
4
SELECT TRANSFORM (<columns>)
USING 'python <python_script>'
AS (<columns>)
FROM <table>;

其中, SELECT 中的 columns 是 FROM 中 table 的列名, 而 AS 中的 columns 是经过 USING 中 Python 脚本 python_script 计算返回的列名。

这里,查找包含指定关键字的 HiveQL 脚本如下:

1
2
3
4
5
6
7
8
9
10
11
SELECT t2.order_sn, t2.delivery_address
FROM

(SELECT TRANSFORM (t1.order_sn, t1.delivery_address)
USING 'python FindSpecifiedWords.py'
AS (order_sn STRING, delivery_address STRING, found STRING)
FROM order_base t1
WHERE t1.pay_time >= UNIX_TIMESTAMP('2015-10-04 00:00:00')
AND t1.pay_time < UNIX_TIMESTAMP('2015-10-05 00:00:00')) t2

WHERE t2.found = 'Y';

为查询方便,可以将 Hive 中注册,以及调用过程一起写在 SQL 脚本 find_specified_order.sql 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
ADD FILE /mnt/whlminds/FindSpecifiedWords.py;

SELECT t2.order_sn, t2.delivery_address
FROM

(SELECT TRANSFORM (t1.order_sn, t1.delivery_address)
USING 'python FindSpecifiedWords.py'
AS (order_sn STRING, delivery_address STRING, found STRING)
FROM order_base t1
WHERE t1.pay_time >= UNIX_TIMESTAMP('2015-10-04 00:00:00')
AND t1.pay_time < UNIX_TIMESTAMP('2015-10-05 00:00:00')) t2

WHERE t2.found = 'Y';

然后,执行 SQL 脚本:

1
hive> hive -f find_specified_order.sql

Java-UDF vs. Python-UDF

上述2部分分别介绍了通过 Java 和 Python 实现 Hive 中 UDF 过程,这里比较下二者的异同:

  • Java 实现 UDF,需要引用包含 Hive API 的外部 jar 包,而 Python 无需引起其他外部包;
  • Java 实现 UDF 后,需要打包后才可被 HiveQL 调用,而通过 Python 实现 UDF 后,可以在 HiveQL 中直接被调用;
  • Java 实现 UDF,对读入和输出数据方式没有要求,实现的 UDF 可以输入一条记录的指定列数据,输出结果可以直接在 HiveQL 的 WHERE 中用于判断条件使用;
  • Python udf 不能处理单列,一次处理一行(若干个列),输出0行或者多行(若干个列)。Java udf 可以。
  • Python udf 需要单独启动一个 python 程序,代价比较大,执行效率比 java 低 80% ~ 90% 左右。
  • Python 实现的 UDF,对读入和输出数据方式有特殊要求,需要对 HiveQL 中表的指定列数据批量读入,然后一对一地批量输出,因此,通过 Python 实现的 UDF 可以结合子查询使用。

总结

Hive 的 UDF 是对 Hive 内置的函数的扩展,本文主要介绍通过 Java 和 Python 实现 Hive 中的 UDF 过程,最后,比较了二者在使用过程中异同。

参考资料

  1. Hive Operators and User-Defined Functions (UDFs)
  2. A Guide To User-Defined Functions In Apache Hive
  3. Use Python with Hive and Pig in HDInsight
<think>嗯,用户之前已经问过自定义UDF函数,现在又问Hive UDF实现,可能需要更具体的步骤。首先,我要确定用户的需求是什么。可能他们现在在使用Hive,遇到了需要扩展功能的情况,而内置函数不够用。用户可能是一名数据工程师或者数据分析师,需要处理大数据,用Hive进行数据清洗或复杂计算。 接下来,我需要回顾Hive UDF的不同类型,比如普通UDF、UDAFUDTF,因为用户可能不清楚这些区别。然后,实现步骤是关键,用户需要具体的代码示例注册方法。用户可能对Java比较熟悉,因为Hive UDF通常用Java编写,但也要提到其他语言的可能性,比如Python,虽然Hive原生支持可能有限。 然后,注意事项部分很重要,比如性能问题、错误处理、版本兼容性。用户可能在开发过程中会遇到性能瓶颈,或者部署时的依赖问题。另外,测试用例也是用户需要的,他们可能想知道如何验证自己的UDF是否正确。 用户可能还有更深的疑问,比如如何优化UDF性能,或者如何处理复杂数据类型,比如数组或Map。所以,在回答时要涵盖这些高级用法,并给出示例。同时,要提醒用户优先使用内置函数,避免不必要的UDF带来的维护成本。 最后,确保结构清晰,步骤分解明确,代码示例正确,并且符合用户之前提到的格式要求,比如数学公式用$$包裹,代码块用正确语法高亮。还要检查是否所有行内公式都用了$符号,独立公式单独成段,避免格式错误。需要确认是否涵盖了用户可能的痛点,比如部署步骤、依赖管理,以及实际应用场景,帮助用户更好地理解应用Hive UDF。</think>好的,我将通过具体步骤介绍Hive UDF实现方法: --- ### **1. Hive UDF基础原理** Hive UDF通过Java类继承特定父类实现,核心处理逻辑发生在`evaluate()`方法中,其执行流程可表示为: $$ \text{输入数据} \xrightarrow{\text{UDF映射}} \text{处理结果} $$ #### **UDF类型对比** | 类型 | 父类 | 功能特点 | |-----------|---------------------|--------------------| | 普通UDF | `UDF` | 一进一出(单行处理) | | 聚合UDF(UDAF)| `UDAF` | 多进一出(分组聚合) | | 表生成UDF(UDTF)| `GenericUDTF` | 一进多出(行转列) | --- ### **2. 标准开发流程** #### **(1) 创建Java类** ```java // 示例:字符串反转UDF package com.hive.udf; import org.apache.hadoop.hive.ql.exec.UDF; import org.apache.hadoop.io.Text; public class ReverseStringUDF extends UDF { public Text evaluate(Text input) { if (input == null) return null; return new Text(new StringBuilder(input.toString()).reverse().toString()); } } ``` #### **(2) 编译打包** ```bash # 需包含Hive依赖(hive-exec.jar) javac -cp /path/to/hive/lib/* ReverseStringUDF.java jar cf udf.jar com/hive/udf/*.class ``` #### **(3) Hive注册** ```sql -- 上传JAR到HDFS(生产环境推荐) ADD JAR /local/path/udf.jar; -- 创建临时函数 CREATE TEMPORARY FUNCTION reverse_udf AS 'com.hive.udf.ReverseStringUDF'; ``` #### **(4) 调用验证** ```sql SELECT reverse_udf('hello'); -- 输出'olleh' ``` --- ### **3. 高级实现技巧** #### **(1) 多参数处理** ```java // 计算两点间距离 public class DistanceUDF extends UDF { public Double evaluate(Double x1, Double y1, Double x2, Double y2) { return Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2)); } } ``` 调用方式:`SELECT distance_udf(116.40,39.90,121.47,31.23)` #### **(2) 复杂类型处理** 处理`ARRAY`类型: ```java public class ArraySumUDF extends UDF { public Double evaluate(List<Double> arr) { return arr.stream().mapToDouble(Double::doubleValue).sum(); } } ``` #### **(3) 优化技巧** - **缓存重用对象**:避免在`evaluate()`中频繁创建对象 - **类型检查**:增加空值处理逻辑 - **使用Writable类型**:正确使用`Text/IntWritable`等Hadoop类型 --- ### **4. 调试与部署** #### **(1) 单元测试** ```java @Test public void testReverseUDF() { ReverseStringUDF udf = new ReverseStringUDF(); assertEquals("cba", udf.evaluate(new Text("abc")).toString()); } ``` #### **(2) 错误排查** - 查看Hive日志:`tail -f /tmp/{user}/hive.log` - 常见错误码: - `SemanticException [Error 10011]: Invalid function` → 类路径错误 - `ClassNotFoundException` → JAR未正确加载 --- ### **5. 实际应用案例** #### **(1) 数据脱敏** ```java // 手机号中间四位脱敏 public String evaluate(String phone) { return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); } ``` #### **(2) JSON解析** ```sql SELECT json_get(info, '$.user.address') FROM log_table; ``` --- ### **6. 注意事项** 1. **性能影响**:UDF会引发MapReduce任务,避免在超大规模数据集逐行调用 2. **版本兼容**:Hive 2.x与3.x的UDF接口存在差异 3. **热部署**:修改UDF后需`RELOAD FUNCTION` 4. **安全限制**:禁止在UDF中执行系统命令 --- **最佳实践建议**: 优先使用Hive内置函数(约300+个),仅在以下场景使用UDF: - 特定业务逻辑(如金融行业计算IRR) - 特殊格式解析(非标准JSON/XML) - 性能敏感型计算(需结合Java本地代码优化)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值