/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
1. Hive UDF/UDAF/UDTF 概述与区别
Hive 提供了三种类型的自定义函数来扩展其数据处理能力:UDF(User-Defined Function)、UDAF(User-Defined Aggregate Function)和 UDTF(User-Defined Table-Generating Function)。
UDF 是最基本的自定义函数类型,它接收单个输入值并返回一个输出值,类似于 SQL 中的标量函数。例如,字符串处理函数、数学计算函数等。
UDAF 是聚合函数,接收多个输入值并返回一个汇总值,如 COUNT、SUM、AVG 等。自定义 UDAF 需要实现特定的接口来处理数据集的聚合操作。
UDTF 与前两者不同,它接收单个输入值但返回多行多列的结果,类似于 EXPLODE 函数,可以将一个复杂类型数据(如数组或 map)展开为多行。
2. 自定义函数开发步骤与实现
开发 Hive 自定义函数的基本步骤如下:
以 UDF 为例,需要继承 UDF 类并实现 evaluate() 方法:
package com.example.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class LowerUDF extends UDF {
public String evaluate(Text str) {
if (str == null) {
return null;
}
return str.toString().toLowerCase();
}
}
UDAF 的实现更复杂,需要继承 GenericUDAFResolver2、AbstractGenericUDAFResolver 或相关的接口,并实现多个方法来处理数据的初始化、迭代、合并和最终化。
3. 复杂数据类型处理方法
Hive 支持多种复杂数据类型,包括数组(ARRAY)、映射(MAP)、结构体(STRUCT)等。自定义函数可以处理这些复杂类型,以实现更灵活的数据转换和分析。
处理数组类型时,可以使用 ArrayWritable 或 ListObjectInspector 来获取数组元素并处理:
public class ArrayElementUDF extends UDF {
public String evaluate(List<String> array, int index) {
if (array == null || index < 0 || index >= array.size()) {
return null;
}
return array.get(index);
}
}
处理映射类型时,可以使用 MapObjectInspector 来获取键值对:
public class MapValueUDF extends UDF {
public String evaluate(Map<String, String> map, String key) {
if (map == null || key == null) {
return null;
}
return map.get(key);
}
}
对于结构体类型,可以使用 StructObjectInspector 来访问结构体的各个字段。
4. 性能优化技巧与实践
自定义函数的性能优化对 Hive 查询的整体性能至关重要。以下是一些关键的优化技巧:
下面是一个优化后的 UDF 示例,使用了缓存机制:
public class CachedComputeUDF extends UDF {
private static Map<String, String> cache = new ConcurrentHashMap<>();
public String evaluate(String input) {
if (input == null) {
return null;
}
// 先从缓存中查找
String cachedResult = cache.get(input);
if (cachedResult != null) {
return cachedResult;
}
// 执行计算
String result = expensiveCompute(input);
// 存入缓存
cache.put(input, result);
return result;
}
private String expensiveCompute(String input) {
// 模拟复杂计算
return input.toUpperCase();
}
}
5. 实际应用案例与注意事项
在实际应用中,自定义函数可以解决许多复杂的数据处理问题。例如,可以开发一个自定义函数来解析 JSON 字符串并提取特定字段,或者实现一个复杂的字符串匹配算法。
注意事项:
最小示例与完整代码
以下是一个完整的 Hive UDF 开发、部署和使用示例:
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>3.1.2</version>
</dependency>
</dependencies>
package com.example.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class ReverseStringUDF extends UDF {
public String evaluate(Text str) {
if (str == null) {
return null;
}
return new StringBuilder(str.toString()).reverse().toString();
}
}
— 添加 JAR 文件
ADD JAR /path/to/your/udf.jar;
— 创建临时函数
CREATE TEMPORARY FUNCTION reverse_string AS 'com.example.hive.udf.ReverseStringUDF';
— 使用函数
SELECT reverse_string(name) FROM employees;
— 创建永久函数
CREATE FUNCTION reverse_string AS 'com.example.hive.udf.ReverseStringUDF' USING JAR 'hdfs:///path/to/your/udf.jar';
这个示例展示了一个简单的字符串反转函数,可以根据实际需求进行扩展和修改。
Mermaid 流程图
下面是一个展示 Hive UDF/UDAF/UDTF 开发流程的 Mermaid 流程图:
#publish-mermaid-0-1789441469308{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-0-1789441469308 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-0-1789441469308 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-0-1789441469308 .error-icon{fill:#552222;}#publish-mermaid-0-1789441469308 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-0-1789441469308 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-0-1789441469308 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-0-1789441469308 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-0-1789441469308 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-0-1789441469308 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-0-1789441469308 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-0-1789441469308 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-0-1789441469308 .marker.cross{stroke:#333333;}#publish-mermaid-0-1789441469308 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-0-1789441469308 p{margin:0;}#publish-mermaid-0-1789441469308 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-0-1789441469308 .cluster-label text{fill:#333;}#publish-mermaid-0-1789441469308 .cluster-label span{color:#333;}#publish-mermaid-0-1789441469308 .cluster-label span p{background-color:transparent;}#publish-mermaid-0-1789441469308 .label text,#publish-mermaid-0-1789441469308 span{fill:#333;color:#333;}#publish-mermaid-0-1789441469308 .node rect,#publish-mermaid-0-1789441469308 .node circle,#publish-mermaid-0-1789441469308 .node ellipse,#publish-mermaid-0-1789441469308 .node polygon,#publish-mermaid-0-1789441469308 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-0-1789441469308 .rough-node .label text,#publish-mermaid-0-1789441469308 .node .label text,#publish-mermaid-0-1789441469308 .image-shape .label,#publish-mermaid-0-1789441469308 .icon-shape .label{text-anchor:middle;}#publish-mermaid-0-1789441469308 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-0-1789441469308 .rough-node .label,#publish-mermaid-0-1789441469308 .node .label,#publish-mermaid-0-1789441469308 .image-shape .label,#publish-mermaid-0-1789441469308 .icon-shape .label{text-align:center;}#publish-mermaid-0-1789441469308 .node.clickable{cursor:pointer;}#publish-mermaid-0-1789441469308 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-0-1789441469308 .arrowheadPath{fill:#333333;}#publish-mermaid-0-1789441469308 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-0-1789441469308 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-0-1789441469308 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-0-1789441469308 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-0-1789441469308 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-0-1789441469308 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-0-1789441469308 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-0-1789441469308 .cluster text{fill:#333;}#publish-mermaid-0-1789441469308 .cluster span{color:#333;}#publish-mermaid-0-1789441469308 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-0-1789441469308 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-0-1789441469308 rect.text{fill:none;stroke-width:0;}#publish-mermaid-0-1789441469308 .icon-shape,#publish-mermaid-0-1789441469308 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-0-1789441469308 .icon-shape p,#publish-mermaid-0-1789441469308 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-0-1789441469308 .icon-shape .label rect,#publish-mermaid-0-1789441469308 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-0-1789441469308 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-0-1789441469308 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-0-1789441469308 .node .neo-node{stroke:#9370DB;}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].node rect,#publish-mermaid-0-1789441469308 [data-look=\”neo\”].cluster rect,#publish-mermaid-0-1789441469308 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789441469308 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1789441469308 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
开始
确定函数类型 UDF/UDAF/UDTF
创建Java项目并添加Hive依赖
继承相应的基类并实现方法
编译并打包为JAR文件
将JAR添加到Hive类路径
创建临时或永久函数
在查询中使用自定义函数
测试和优化性能
部署到生产环境
结束
表格对比
下面是一个对比 UDF、UDAF 和 UDTF 的表格:
| UDF | 单个值 | 单个值 | 数据转换、格式化 | 低 | LOWER()、UPPER() |
| UDAF | 多个值 | 单个值 | 数据聚合、统计 | 中 | SUM()、AVG()、COUNT() |
| UDTF | 单个值 | 多行多列 | 数据展开、拆分 | 中高 | EXPLODE()、POSEXPLODE() |
