大数据Hive中的UDF:自定义数据处理的利器(上)


文章目录

    • 1. 前言
    • 2. UDF与宏及静态表的对比
    • 3. 深入理解UDF
    • 4. 实现自定义UDF

1. 前言

在大数据技术栈中,Apache Hive 扮演着数据仓库的关键角色,它提供了丰富的数据操作功能,并通过类似于 SQLHiveQL 语言简化了对 Hadoop 数据的处理。然而,内置函数库虽强大,却未必能满足所有特定的业务逻辑需求。此时,用户定义函数(User-Defined FunctionsUDF)的重要性便凸显出来。

Hive UDFUser-Defined Function)是Hive中的一种扩展机制,它允许用户通过编写自定义的Java代码来扩展Hive的功能,实现Hive内置函数无法提供的一些特定数据处理逻辑。

2. UDF与宏及静态表的对比

除了UDF可以自定义输入和输出还有例如静态表,宏定义的方式也可以实现类似的操作,举个例子:在数据中筛选出已达到退休年龄的员工。

UDF 示例

import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;public class RetirementStatusUDF extends UDF {public Text evaluate(int age) {return new Text(age >= 60 ? "已达到退休年龄" : "未达到退休年龄");}
}-- 添加UDF的jar包到Hive
ADD JAR /path/to/udf.jar;-- 创建临时函数
CREATE TEMPORARY FUNCTION retirement_status AS 'com.example.RetirementStatusUDF';-- 使用UDF进行查询
SELECT name, age, retirement_status(age) AS status
FROM employee_static;

静态表


CREATE TABLE employee_static (age INT,flag STRING -- 'retired' 或 'active'
);-- 查询已达到退休年龄的员工
SELECT a.*
FROM employee_data a
JOIN employee_static b
ON a.age = b.age AND b.flag = 'retired';

宏定义

drop temporary macro if exists get_retired;create temporary macro get_retired(age bigint) 
if (age is not null,casewhen age >= 60 then '退休'when age <= 60 then '未退休'else nullend,null
);

使用UDF(用户定义函数)的原因与宏和静态表的功能有所不同,它们各自适用于不同的场景和需求。以下是使用UDF的几个关键原因:

特性/方法UDF(用户定义函数)宏(Macro)静态表(Static Table)
定义允许用户通过编写自定义的Java代码来扩展Hive的功能,实现特定的数据处理逻辑。在Hive中,宏是一种用户定义的快捷方式,用于封装一系列HiveQL语句,以便在查询中重复使用。预先定义和填充的数据集,其结构和内容在创建后通常保持不变。
使用场景适用于执行Hive内置函数不支持的特定数据处理逻辑,如复杂的业务规则或算法。主要用于简化和重用HiveQL查询语句,提高代码的可读性和易维护性。适用于存储已知的、不变的数据集,供多次查询使用,无需每次重新计算。
灵活性高,可以根据需求定制数据处理流程。中等,主要用于简化复杂的查询,但不具备动态处理能力。低,结构和内容一旦定义,通常不发生变化。
性能可优化,Hive执行UDF时会进行优化,性能接近内置函数。取决于宏定义的查询的复杂性,可能提高或降低性能。预先计算,查询时性能较高,适合重复查询相同数据集。
重用性高,一旦创建和注册,可以在不同的Hive会话中重复使用。高,宏可以定义一次并在多个查询中重复使用。中等,表结构和数据不变,适用于重复查询相同数据集的场景。
实时性支持实时数据处理,每次调用UDF时根据输入动态执行计算。不直接支持实时数据处理,主要用于查询语句的封装。不支持实时数据处理,通常是预先计算和存储的。
适应性强,可以快速适应新的数据处理需求。中等,需要修改宏定义以适应新的需求。弱,结构和数据固定,不适合频繁变化的数据需求。
示例应用用于实现如复杂数学计算、自定义字符串处理、数据清洗等。用于封装复杂的查询模板,如多步骤的数据转换过程。用于存储配置数据、参考数据或不需要频繁更新的数据。

选择使用UDF、宏还是静态表应基于具体的业务需求、数据特性和性能考虑。每种方法都有其独特的优势和适用场景。

3. 深入理解UDF

Hive UDF可以分为三种主要类型:UDF、UDAF和UDTF。

  1. UDF (User-Defined Function)
    • 标量函数,用于一对一(one-to-one)的映射,即对单个数据项进行操作并返回单个结果。
    • 例如,字符串处理(upper, substr)、数学计算(sqrt)、日期时间转换等。
  2. UDAF (User-Defined Aggregate Function)
    • 聚合函数,用于多对一(many-to-one)的映射,即对多行数据进行聚合操作并返回单个结果。
    • 例如,自定义的求和(sum)、平均值(avg)、最大值(max)、最小值(min)等。
  3. UDTF (User-Defined Table-Generating Function)
    • 表生成函数,用于一对多(one-to-many)的映射,即对单个数据项进行操作并返回多行结果。
    • 例如,explode函数可以将数组或Map类型的列拆分成多行。
类别简称全称描述示例
UDFUser-Defined Function用于实现一对一的映射,即一个输入对应一个输出。将字符串转换为大写。
UDAFUser-Defined Aggregate Function用于实现一对多的映射,即多个输入对应一个输出。计算某个字段的总和或平均值。
UDTFUser-Defined Table-Generating Function用于实现一对多的行生成,即一个输入可以产生多行输出。将数组或映射类型的字段展开成多行数据。

这些UDF类型允许开发者根据特定的数据处理需求,编写和实现自定义的函数逻辑,从而扩展Hive的数据处理能力。通过使用UDF、UDAF和UDTF,用户可以在Hive中实现更加复杂和定制化的数据处理任务。

实现一个UDF通常涉及以下步骤:

  1. 编写UDF类:在Java中创建一个类,实现Hive UDF接口的相应方法。对于标量UDF,这通常是evaluate方法。
  2. 编译与打包:将UDF类编译成Java字节码,并打包成JAR文件。
  3. 上传JAR包:将JAR文件上传到HDFS或其他Hive可以访问的文件系统中。
  4. 注册UDF:在Hive会话中使用ADD JARCREATE TEMPORARY FUNCTION命令注册UDF。
  5. 使用UDF:在Hive查询中调用注册的UDF,就像调用内置函数一样。

4. 实现自定义UDF

在深入探讨Hive UDF的实现之前,让我们首先确保开发环境的准备妥当。对于UDF的编写,推荐使用Maven来配置Java项目,这样可以方便地管理依赖和构建过程。以下是配置Java开发环境的一个示例,包括使用的版本信息和Maven设置:

Apache Maven 3.9.6
Java version: 1.8.0_211,


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>project202401</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><hadoop.version>3.1.1.7.1.7.2000-305</hadoop.version><hive.version>3.1.3000.7.1.7.2000-305</hive.version></properties><dependencies><dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>${hive.version}</version><scope>provided</scope></dependency><dependency><groupId>org.apache.hadoop</groupId><artifactId>hadoop-common</artifactId><version>${hadoop.version}</version><scope>provided</scope></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.10.1</version><scope>test</scope></dependency></dependencies><repositories><repository><id>central</id><name>Maven Central</name><url>https://repo1.maven.org/maven2/</url></repository><repository><id>cloudera</id><name>Cloudera Repository</name><url>https://repository.cloudera.com/artifactory/cloudera-repos/</url></repository></repositories></project>

下面是一个Hive UDF(用户定义函数)的示例,该UDF的作用是将传入的字符串转换为大写形式。我将对代码进行注释,并解释其工作流程:


import org.apache.hadoop.io.Text;  // 引入Hadoop的Text类,用于处理字符串
import org.apache.hadoop.hive.ql.exec.UDF;  // 引入Hive的UDF类@SuppressWarnings({"deprecation", "unused"})  // 忽略警告,例如未使用的警告或过时API的警告
public class UpperCaseUDF extends UDF {  // 定义一个名为UpperCaseUDF的类,继承自UDF/*** 该方法重写了UDF类中的evaluate方法,是UDF的核心。* 它接收一个Text类型的数据,然后返回转换为大写的Text类型数据。** @param line Text类型的输入数据* @return 转换为大写的Text类型的数据*/public Text evaluate(final Text line) {// 检查传入的Text是否为非空且内容不为空字符串if (null != line && !line.toString().equals("")) {// 将Text转换为String,并使用String的toUpperCase方法转换为大写String str = line.toString().toUpperCase();// 将大写字符串重新设置回Text对象,并返回line.set(str);return line;} else {// 如果传入的Text为null或空字符串,则返回一个新的空Text对象return new Text();}}
}

在Hive的较新版本中,推荐使用GenericUDF而不是直接继承UDF。以下是使用GenericUDF实现的UpperCaseUDF2的示例代码,以及对代码的详细解释和工作流程分析:


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.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.StringObjectInspector;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.hive.ql.udf.UDFType;@UDFType(deterministic = true, stateful = false) // 标注UDF的特性,确定性且无状态
public class UpperCaseUDF2 extends GenericUDF { // 继承自GenericUDFprivate StringObjectInspector inputOI; // 输入对象检查员,用于检查输入类型private StringObjectInspector outputOI; // 输出对象检查员,用于定义输出类型/*** initialize方法在UDF首次执行时被调用,用于初始化UDF。* @param arguments 传入的参数对象检查员数组* @return 输出对象检查员* @throws UDFArgumentException 如果输入参数不符合预期,抛出异常*/@Overridepublic ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {// 确保传入的参数是字符串类型if (arguments == null || arguments.length == 0) {throw new UDFArgumentException("arguments array is null or empty.");}if (!(arguments[0] instanceof StringObjectInspector)) {// 如果不是字符串类型,抛出Hive异常throw new UDFArgumentException("The input to UpperCaseUDF2 must be a string");}// 将输入参数的对象检查员赋值给局部变量inputOI = (StringObjectInspector) arguments[0];// 定义输出对象检查员为可写的字符串对象检查员outputOI = PrimitiveObjectInspectorFactory.writableStringObjectInspector;// 返回输出对象检查员return outputOI;}/*** evaluate方法定义了UDF的实际逻辑,即如何将输入转换为输出。* @param arguments 包含延迟计算的输入对象的数组* @return 转换后的大写文本* @throws HiveException 如果在执行过程中遇到Hive异常*/@Overridepublic Object evaluate(DeferredObject[] arguments) throws HiveException {// 从延迟对象中获取输入字符串Text line = (Text) arguments[0].get();// 如果输入不为空,则转换为大写if (line != null && !line.toString().isEmpty()) {return new Text(line.toString().toUpperCase());}// 如果输入为空,返回空字符串return new Text();}/*** getDisplayString方法返回UDF的可读字符串表示,用于Hive日志和解释计划。* @param strings 输入参数的字符串表示,通常由Hive自动生成* @return UDF的可读字符串表示*/@Overridepublic String getDisplayString(String[] strings) {// 返回UDF的名称,用于解释计划和日志return "UpperCaseUDF2()";}
}
add jar URL/project202401-1.0-SNAPSHOT.jar;
create temporary function UpperCaseUDF as 'com.xx.hive.udf.UpperCaseUDF';
select UpperCaseUDF('Hive Is Fun') a ;

HIVE IS FUN

通过以上步骤,我们能够创建出高效、可靠的Hive UDF,以满足特定的数据处理需求。UDF的开发不仅需要关注功能的实现,还要重视性能优化和代码的可维护性。正确地使用UDF可以显著提升数据处理的效率,为用户提供强大的数据操作能力。

因为篇幅有限,后面两种自定义UDF,会在下一篇博文展开叙述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/637765.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

认知觉醒 PDF电子版 下载

认知觉醒 PDF电子版 开启自我改变的原动力 周岭 / 人民邮电出版社 / 2020-10 链接&#xff1a;https://pan.baidu.com/s/1EHUK_AhvE5TWAZsYXFQ5QA?pwdwrho 提取码&#xff1a;wrho

【PyTorch】torch.gather() 用法

gather常被用于image做mask的操作中&#xff0c;对哪些地方进行赋值0/1 API&#xff1a; torch.gather — PyTorch 2.2 documentation torch.gather(input, dim, index, outNone) → Tensor gather()的意义&#xff1a; 顾名思义&#xff0c;聚集、集合&#xff1a;gather…

OpenUI在windows下部署使用

OpenUI OpenUI是一个基于Python的AI对话平台&#xff0c;支持接入多种AI模型。 通过聊天的方式来进行UI设计&#xff0c;你可以通过文字来描述你想要的UI界面&#xff0c;OpenUI可以帮你实时进行渲染出效果 安装OpenUI 这里预设你的电脑上已安装git、Python和pip&#xff0…

分类神经网络2:ResNet模型复现

目录 ResNet网络架构 ResNet部分实现代码 ResNet网络架构 论文原址&#xff1a;https://arxiv.org/pdf/1512.03385.pdf 残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的&#xff0c;通过引入残差学习解决了深度网络训练中的退化问题&#xff…

Nodejs - 异步I/O

异步I/O 利用单线程&#xff0c;远离多线程死锁&#xff0c;状态同步等问题&#xff0c;利用异步I/O&#xff0c; 让单线程原理阻塞&#xff0c;更好的使用cpu异步I/O实现现状 阻塞IO 操作系统内对于I/O只有两种方式: 阻塞和非阻塞。在调用阻塞I/O的时候&#xff0c;应用程序需…

能源成果3D网络三维展厅越发主流化

在这个数字化飞速发展的时代&#xff0c;我们为您带来了全新的展览形式——线上3D虚拟展厅。借助VR虚拟现实制作和web3d开发技术&#xff0c;我们能够将物品、图片、视频和图文信息等完美融合&#xff0c;通过计算机技术和3D建模&#xff0c;为您呈现一个逼真、生动的数字化展览…

Llama 3大模型发布!快速体验推理及微调

Meta&#xff0c;一家全球知名的科技和社交媒体巨头&#xff0c;在其官方网站上正式宣布了一款开源的大型预训练语言模型——Llama-3。 据了解&#xff0c;Llama-3模型提供了两种不同参数规模的版本&#xff0c;分别是80亿参数和700亿参数。这两种版本分别针对基础的预训练任务…

JDBC学习

DriverManager&#xff08;驱动管理类&#xff09; Drivermanager的作用有&#xff1a; 1.注册驱动&#xff1b; 2.获取数据库连接 Class.forName("com.mysql.cj.jdbc.Driver"); 这一行的作用就是注册Mysql驱动&#xff08;把我们下载的jar包加载到内存里去&…

地图图源#ESRI ArcGIS XYZ Tiles系列(TMS)

目录 1、前言 2、地图图源网址 2.1、Satellite 卫星图源 2.2、Terrain 地形图源 2.3、Street 路网/标注图源 2.4、Specifity 特色设计图源 3、专业推荐”穿搭“ 4、图源配置下载及使用 图源名称图层类别特别注意谷歌 Google①地形 ②影像 ③矢量及标注 ④特色图源国内大…

【讲解下Spring Boot单元测试】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

HotSpot JVM 中的应用程序/动态类数据共享

0.前言 本文的目的是详细讨论 HotSpot JVM 自 JDK 1.5 以来提供的一项功能&#xff0c;该功能可以减少启动时间&#xff0c;但如果在多个 JVM 之间共享相同的类数据共享 (CDS) 存档&#xff0c;则还可以减少内存占用。 1.类数据共享 (CDS) CDS 的想法是使用特定格式将预处理…

【LeetCode】187. 重复的DNA序列

题目链接&#xff1a;187. 重复的DNA序列 题目描述&#xff1a; 思路&#xff1a;首先要明白一个重要概念区别&#xff1a; 子串(substring): 原始字符串的一个连续子集。子序列&#xff08;subsequence): 原始字符串的一个子集。 再注意题意是&#xff1a;长度为10的子串出现…