场景:
hive的内置函数满足不了所有的业务需求。
hive提供很多的模块可以自定义功能,比如:自定义函数、serde、输入输出格式等。
hive提供的自定义函数:
UDF:user defined function,用户自定函数,一行输入一行输出。类似于普通函数,如size,length
UDAF:user defined aggregate function,用户自定义聚合函数,多行输入一行输出。类似sum
UDTF:user defined table-gernarate function,用户自定义表生成函数,一行输入多行输出。类似炸裂
UDF案例
定义UDF函数要注意下面几点:
1. 继承`org.apache.hadoop.hive.ql.exec.UDF`
2. 重写`evaluate`(),这个方法不是由接口定义的,因为它可接受的参数的个数,数据类型都是不确定的。Hive会检查UDF,看能否找到和函数调用相匹配的evaluate()方法。
3、`evaluate`()允许重载。
UDF案例
pom.xml
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.3.7</version>
</dependency>
FirstFun
package com.al.hive;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.exec.UDF;
/**
* 第一个自定义函数.
* 做两个数的拼接
*/
public class FirstFun extends UDF {
//重写evaluate,名字不能错
//String:方法返回值类型
//String a,String b :方法的输入参数和类型
public String evaluate(String a,String b){
if(StringUtils.isEmpty(a)){
return null;
}
if(StringUtils.isEmpty(b)){
return null;
}
//数据正常
return a+"_"+b;
}
//重载
public String evaluate(String a){
if(StringUtils.isEmpty(a)){
return null;
}
//数据正常
return a;
}
}
这种加载只对本session有效
# 将编写的udf的jar包上传到服务器上.
# 并且将jar包添加到hive的class path中
# 进入到hive客户端,执行下面命令
hive> add jar /home/qf-2102-1.0.jar;
-- 创建一个临时函数名,要跟上面hive在同一个session里面:
hive> create temporary function myConcat as 'com.al.hive.FirstFun';
-- 检查函数是否创建成功
hive> show functions;
-- 查看函数使用方法
hive> desc function myconcat;
-- 测试功能
hive (test)>select myconcat('cc','ad');
OK
cc_ad
Time taken: 0.806 seconds, Fetched: 1 row(s)
hive (test)> select myconcat('cc');
OK
cc
Time taken: 0.09 seconds, Fetched: 1 row(s)
hive (test)> select myconcat('cc','');
OK
NULL
-- 删除函数
hive> drop temporary function if exists myconcat;
配置文件加载
通过配置文件方式这种只要用hive命令行启动都会加载函数
# 1、将编写的udf的jar包上传到服务器上
# 2、在hive的安装目录的bin目录下创建一个配置文件,文件名:.hiverc
[root@01 hive]# vi ./.hiverc
add jar /home/qf-2102-1.0.jar;
create temporary function myConcat as 'com.al.hive.FirstFun';
3、启动hive
[root@01 hive]# hive
hive> select myconcat('cc','ad');
注册永久函数
将jar包上传到hdfs的目录中。
# 1、将编写的udf的jar包上传到hdfs上
[root@hadoop01 ~]# hdfs dfs -mkdir /auxjars
[root@hadoop01 ~]# hdfs dfs -put /home/qf-2102-1.0.jar /auxjars
create function test.myConcat as 'com.al.hive.FirstFun' using jar 'hdfs://hadoop01:9000/auxjars/qf-2102-1.0.jar';
3、启动hive
[root@q01 hive]# hive
hive> select myConcat('cc','ad');
Added [/tmp/bc4a1229-2354-4ce5-ad8c-24cdf9df7a1f_resources/qf-2102-1.0.jar] to class path
Added resources: [hdfs://hadoop01:9000/auxjars/qf-2102-1.0.jar]
OK
cc_ad
UDTF案例
UDTF是一对多的输入输出,实现UDTF需要完成下面步骤
继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
重写initlizer()、process()、close()。
执行流程如下:
UDTF首先会调用initialize方法,此方法返回UDTF的返回行的信息(返回个数,类型)。
初始化完成后,会调用process方法,真正的处理过程在process函数中,在process中,每一次forward()调用产生一行;如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。
最后close()方法调用,对需要清理的方法进行清理。
/*把"k1:v1;k2:v2;k3:v3"类似的的字符串解析成每一行多行,每一行按照key:value格式输出
kv2clo("k1:v1;k2:v2;k3:v3")
k1:v1
k2:v2
k3:v3
*/
package com.al.hive;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTFExplode;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import java.util.ArrayList;
import java.util.List;
/**
* 表生成函数
*/
public class MyUDTF extends GenericUDTF {
//在initializez中初始化要输出字段的名称和类型
@Override
public StructObjectInspector initialize(ObjectInspector[] argOIs) throws UDFArgumentException {
//定义要输出列的名字的List,并且添加要输出的列名
List<String> structFieldNames = new ArrayList<String>();
structFieldNames.add("key");
structFieldNames.add("value");
//定义要输出列的类型的List,并且添加要输出列的类型
List<ObjectInspector> objectInspectorList = new ArrayList<ObjectInspector>();
objectInspectorList.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
objectInspectorList.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
//定义返回的schema
return ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, objectInspectorList);
}
/**
* 核心处理方法
* @param objects
* @throws HiveException
*/
public void process(Object[] objects) throws HiveException {
//得到第一个参数,转化为字符串,类似于-> name:zhang;age:30;address:shenzhen
String insputString = objects[0].toString();
//把上述例子字符串按照分号;切分为数组
String[] split = insputString.split(";");
//s=name:zhang
for (String s : split) {
//把每个切分后的key value分开
String[] kvArray = s.split(":");
//如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。
forward(kvArray);
}
}
//关闭对象
public void close() throws HiveException {
}
}
UDAF案例
用户自定义聚合函数。user defined aggregate function。多对一的输入输出 count sum max。定义一个UDAF需要如下步骤:
UDF自定义函数必须是org.apache.hadoop.hive.ql.exec.UDAF的子类,并且包含一个有多个嵌套的的实现了org.apache.hadoop.hive.ql.exec.UDAFEvaluator的静态类。
函数类需要继承UDAF类,内部类Evaluator实UDAFEvaluator接口。
Evaluator需要实现 init、iterate、terminatePartial、merge、terminate这几个函数
这几个函数作用如下:
函数 说明
init 实现接口UDAFEvaluator的init函数
iterate 每次对一个新值进行聚集计算都会调用,计算函数要根据计算的结果更新其内部状 态,map端调用
terminatePartial 无参数,其为iterate函数轮转结束后,返回轮转数据,,map端调用
merge 接收terminatePartial的返回结果,进行数据merge操作,其返回类型为boolean。
terminate 返回最终的聚集函数结果。 在reducer端调用
//计算一组整数的最大值
package com.al.hive;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
import org.apache.hadoop.io.IntWritable;
//注解方法的描述,,desc function name
@Description(value = "findmax(col)",name = "findmax",extended = "this is find max function")
public class MyUDAF extends UDAF {
//UDAF要求 并且包含一个或者多个嵌套的的实现了
// org.apache.hadoop.hive.ql.exec.UDAFEvaluator的静态类。
public static class MaxnumIntUDAFEvaluator implements UDAFEvaluator {
//在静态类内部定义一个返回值,作为当前UDAF最后的唯一返回值,因为返回值要在hive调用,所以必须要使用序列化类型
private IntWritable result;
/**
* 在初始化是把返回值设为null,避免和上次调用时混淆
*/
public void init() {
result=null;
}
/*
A 3
A 1
V 5
A 5
ite(1,1,1)
*/
//定义一个函数iterate用来处理遍历多行时,每行值传进来是调用的函数
public boolean iterate(IntWritable value) {
//把遍历每行的值value传入,和result比较,如果比result大,那么result就设置为value,否则result不变
if (value == null) {
return true;
}
//如果是第一行数据,那么直接给result赋值为第一行数据
if (result == null) {
result = new IntWritable(value.get());
} else {
//给result赋值result和value之间的最大值
result.set(Math.max(result.get(),value.get()));
}
return true;
}
/**
* 在map端进行并行执行后的结果
* @return
*/
public IntWritable terminatePartial() {
return result;
}
/**
* 接收terminatePartial的返回结果,进行数据merge操作,其返回类型为boolean。
* @param other
* @return
*/
public boolean merge(IntWritable other) {
return iterate(other);
}
/**
* 将最终的结果返回为Hive
* @return
*/
public IntWritable terminate() {
return result;
}
}
}