Hive UDF函数构建
1. 概述
UDF函数其实就是一个简单的函数,执行过程就是在Hive转换成MapReduce程序后,执行java方法,类似于像MapReduce执行过程中加入一个插件,方便扩展。UDF只能实现一进一出的操作,如果需要实现多进一出,则需要实现UDAF。
Hive可以允许用户编写自己定义的函数UDF,来在查询中使用。
2. UDF类型
Hive中有3种UDF:
UDF:操作单个数据行,产生单个数据行;
UDAF:操作多个数据行,产生一个数据行;
UDTF:操作一个数据行,产生多个数据行一个表作为输出;
3. 如何构建UDF
用户构建的UDF使用过程如下:
- 继承UDF或者UDAF或者UDTF,实现特定的方法;
- 将写好的类打包为jar,如LowerUDF.jar;
- 进入到Hive shell环境中,输入命令add jar /home/hadoop/LowerUDF.jar注册该jar文件;或者把LowerUDF.jar上传到hdfs,hadoop fs -put LowerUDF.jar /home/hadoop/LowerUDF.jar,再输入命令add jar hdfs://hadoop01:8020/user/home/LowerUDF.jar;
- 为该类起一个别名,create temporary function lower_udf as 'UDF.lowerUDF';注意,这里UDF只是为这个Hive会话临时定义的;
- 在select中使用lower_udf();
4. 自定义UDF
4.1 pom.xml依赖
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.2.</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.</version>
</dependency>
4.2 编写UDF代码
package UDF;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text; public class LowerUDF extends UDF{
/**
* 1. Implement one or more methods named "evaluate" which will be called by Hive.
*
* 2. "evaluate" should never be a void method. However it can return "null" if needed.
*/
public Text evaluate(Text str){
// input parameter validate
if(null == str){
return null ;
} // validate
if(StringUtils.isBlank(str.toString())){
return null ;
} // lower
return new Text(str.toString().toLowerCase()) ;
} }
4.3 打包
注意:工程所用的jdk要与Hadoop集群使用的jdk是同一个版本。
4.4 注册UDF
hive> add jar /home/hadoop/LowerUDF.jar
hive> create temporary function lower_udf as "UDF.LowerUDF";
4.5 测试
hive> create table test (id int ,name string);
hive> insert into test values(,'TEST');
hive> select lower_udf(name) from test;
OK
test
注意事项:
- 一个用户UDF必须org.apache.hadoop.hive.ql.exec.UDF;
- 一个UDF必须要包含有evaluate()方法,但是该方法并不存在于UDF中。evaluate的参数个数以及类型都是用户自定义的。在使用的时候,Hive会调用UDF的evaluate()方法。
5. 自定义UDAF
UDAF是聚合函数,相当于reduce,将表中多行数据聚合成一行结果
UDAF是需要在hive的sql语句和group by联合使用,hive的group by 对于每个分组,只能返回一条记录,这点和mysql不一样。
开发通用UDAF有两个步骤:
- resolver负责类型检查,操作符重载,里面创建evaluator类对象;
- evaluator真正实现UDAF的逻辑;
5.1 继承AbstractAggregationBuffer和实现evaluator
package cn.wisec.meerkat.analyseOnHive; import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFParameterInfo;
import org.apache.hadoop.hive.ql.util.JavaDataModel;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.LongObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable; /**
* @Author: Yang JianQiu
* @Date: 2019/7/16 10:48
*
* 开发通用UDAF有两个步骤:
* 第一个是编写resolver类,第二个是编写evaluator类
* resolver负责类型检查,操作符重载
* evaluator真正实现UDAF的逻辑。
* 通常来说,顶层UDAF类继承{@link org.apache.hadoop.hive.ql.udf.generic.GenericUDAFResolver2}
* 里面编写嵌套类evaluator实现UDAF的逻辑
*
* resolver通常继承org.apache.hadoop.hive.ql.udf.GenericUDAFResolver2,但是更建议继承AbstractGenericUDAFResolver,隔离将来hive接口的变化
* GenericUDAFResolver和GenericUDAFResolver2接口的区别是: 后面的允许evaluator实现利用GenericUDAFParameterInfo可以访问更多的信息,例如DISTINCT限定符,通配符(*)。
*/
public class CountUDAF extends AbstractGenericUDAFResolver { /**
* 构建方法,传入的是函数指定的列
* @param params
* @return
* @throws SemanticException
*/
@Override
public GenericUDAFEvaluator getEvaluator(TypeInfo[] params) throws SemanticException {
if (params.length > 1){
throw new UDFArgumentLengthException("Exactly one argument is expected");
}
return new CountUDAFEvaluator();
} /**
* 这个构建方法可以判输入的参数是*号或者distinct
* @param info
* @return
* @throws SemanticException
*/
@Override
public GenericUDAFEvaluator getEvaluator(GenericUDAFParameterInfo info) throws SemanticException { ObjectInspector[] parameters = info.getParameterObjectInspectors();
boolean isAllColumns = false;
if (parameters.length == 0){
if (!info.isAllColumns()){
throw new UDFArgumentException("Argument expected");
} if (info.isDistinct()){
throw new UDFArgumentException("DISTINCT not supported with");
}
isAllColumns = true;
}else if (parameters.length != 1){
throw new UDFArgumentLengthException("Exactly one argument is expected.");
}
return new CountUDAFEvaluator(isAllColumns);
} /**
* GenericUDAFEvaluator类实现UDAF的逻辑
*
* enum Mode运行阶段枚举类
* PARTIAL1;
* 这个是mapreduce的map阶段:从原始数据到部分数据聚合
* 将会调用iterate()和terminatePartial()
*
* PARTIAL2:
* 这个是mapreduce的map端的Combiner阶段,负责在map端合并map的数据:部分数据聚合
* 将会调用merge()和terminatePartial()
*
* FINAL:
* mapreduce的reduce阶段:从部分数据的聚合到完全聚合
* 将会调用merge()和terminate()
*
* COMPLETE:
* 如果出现了这个阶段,表示mapreduce只有map,没有reduce,所以map端就直接出结果了;从原始数据直接到完全聚合
* 将会调用iterate()和terminate()
*/
public static class CountUDAFEvaluator extends GenericUDAFEvaluator{ private boolean isAllColumns = false; /**
* 合并结果的类型
*/
private LongObjectInspector aggOI; private LongWritable result; public CountUDAFEvaluator() {
} public CountUDAFEvaluator(boolean isAllColumns) {
this.isAllColumns = isAllColumns;
} /**
* 负责初始化计算函数并设置它的内部状态,result是存放最终结果的
* @param m 代表此时在map-reduce哪个阶段,因为不同的阶段可能在不同的机器上执行,需要重新创建对象partial1,partial2,final,complete
* @param parameters partial1或complete阶段传入的parameters类型是原始输入数据的类型
* partial2和final阶段(执行合并)的parameters类型是partial-aggregations(既合并返回结果的类型),此时parameters长度肯定只有1了
* @return ObjectInspector
* 在partial1和partial2阶段返回局部合并结果的类型,既terminatePartial的类型
* 在complete或final阶段返回总结果的类型,既terminate的类型
* @throws HiveException
*/
@Override
public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {
super.init(m, parameters);
//当是combiner和reduce阶段时,获取合并结果的类型,因为需要执行merge方法
//merge方法需要部分合并的结果类型来取得值
if (m == Mode.PARTIAL2 || m == Mode.FINAL){
aggOI = (LongObjectInspector) parameters[0];
} //保存总结果
result = new LongWritable(0);
//局部合并结果的类型和总合并结果的类型都是long
return PrimitiveObjectInspectorFactory.writableLongObjectInspector;
} /**
* 定义一个AbstractAggregationBuffer类来缓存合并值
*/
static class CountAgg extends AbstractAggregationBuffer{
long value; /**
* 返回类型占的字节数,long为8
* @return
*/
@Override
public int estimate() {
return JavaDataModel.PRIMITIVES2;
}
} /**
* 创建缓存合并值的buffer
* @return
* @throws HiveException
*/
@Override
public AggregationBuffer getNewAggregationBuffer() throws HiveException {
CountAgg countAgg = new CountAgg();
reset(countAgg);
return countAgg;
} /**
* 重置合并值
* @param agg
* @throws HiveException
*/
@Override
public void reset(AggregationBuffer agg) throws HiveException {
((CountAgg) agg).value = 0;
} /**
* map时执行,迭代数据
* @param agg
* @param parameters
* @throws HiveException
*/
@Override
public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {
//parameters为输入数据
//parameters == null means the input table/split is empty
if (parameters == null){
return;
}
if (isAllColumns){
((CountAgg) agg).value ++;
}else {
boolean countThisRow = true;
for (Object nextParam: parameters){
if (nextParam == null){
countThisRow = false;
break;
}
}
if (countThisRow){
((CountAgg) agg).value++;
}
}
} /**
* 返回buffer中部分聚合结果,map结束和combiner结束执行
* @param agg
* @return
* @throws HiveException
*/
@Override
public Object terminatePartial(AggregationBuffer agg) throws HiveException {
return terminate(agg);
} /**
* 合并结果,combiner或reduce时执行
* @param agg
* @param partial
* @throws HiveException
*/
@Override
public void merge(AggregationBuffer agg, Object partial) throws HiveException {
if (partial != null){
//累加部分聚合的结果
((CountAgg) agg).value += aggOI.get(partial);
}
} /**
* 返回buffer中总结果,reduce结束执行或者没有reduce时map结束执行
* @param agg
* @return
* @throws HiveException
*/
@Override
public Object terminate(AggregationBuffer agg) throws HiveException {
//每一组执行一次(group by)
result.set(((CountAgg) agg).value);
//返回writable类型
return result;
}
}
}
使用:
hive> add jar /root/udf.jar
hive> create temporary function mycount as 'udf.CountUDAF'
hive> select call, mycount(*) as cn from beauty group by call order by cn desc
hive> select tag, mycount(tag) as cn from beauty lateral view explode(tags) lve_beauty as tag group by tag order by cn desc
6. 自定义UDTF
UDTF用来解决输入一行输出多行的需求。
限制:
- No other expressions are allowed in SELECT不能和其他字段一起使用:SELECT pageid,explode(adid_list) AS myCol... is not supported
- UDTF's can't be nested 不能嵌套:SELECT explode(explode(adid_list)) AS myCol..... is not supported
- GROUP BY/ CLUSTER BY/ DISTRIBUTE BY/ SORT BY is not supported:SELECT explode(adid_list) AS myCol.....GROUP BY myCol is not supported
继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF,实现initialize,process,close三个方法。
执行过程:
- UDTF首先会调用initialize方法,此方法返回UDTF的输出行的信息(输出列个数与类型);
- 初始化完成后,会调用process方法,真正的处理过程在process函数中:在process中,每一次forward()调用产生一行;如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。
- 最后close()方法调用,对需要清理的方法进行清理。
下面是实现一个explode函数的例子:
explode会将一个数组中每个元素都输出一行,map中每对key-value都输出一行,实现对数据展开
package cn.wisec.meerkat.analyseOnHive; import org.apache.hadoop.hive.ql.exec.TaskExecutionException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
import org.apache.hadoop.hive.serde2.objectinspector.*; import java.util.ArrayList;
import java.util.List;
import java.util.Map; /**
* @Author: Yang JianQiu
* @Date: 2019/7/16 17:08
*/
public class MyExplodeUDTF extends GenericUDTF { private transient ObjectInspector inputOI = null; /**
* 初始化
* 构建一个StructObjectInspector类型用于输出
* 其中struct的字段构成输出的一行
* 字段名称不重要,因为它们将被用户提供的列别名覆盖
* @param argOIs
* @return
* @throws UDFArgumentException
*/
@Override
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//得到结构体的字段
List<? extends StructField> inputFields = argOIs.getAllStructFieldRefs();
ObjectInspector[] udfInputOIs = new ObjectInspector[inputFields.size()];
for (int i = 0; i < inputFields.size(); i++){
//字段类型
udfInputOIs[i] = inputFields.get(i).getFieldObjectInspector();
} if (udfInputOIs.length != 1){
throw new UDFArgumentLengthException("explode() takes only one argument");
} List<String> fieldNames = new ArrayList<>();
List<ObjectInspector> fieldOIs = new ArrayList<>();
switch (udfInputOIs[0].getCategory()){
case LIST:
inputOI = udfInputOIs[0];
//指定list生成的列名,可在as后覆写
fieldNames.add("col");
//获取list元素的类型
fieldOIs.add(((ListObjectInspector) inputOI).getListElementObjectInspector());
break;
case MAP:
inputOI = udfInputOIs[0];
//指定map中key的生成的列名,可在as后覆写
fieldNames.add("key");
//指定map中value的生成的列名,可在as后覆写
fieldNames.add("value");
//得到map中key的类型
fieldOIs.add(((MapObjectInspector)inputOI).getMapKeyObjectInspector());
//得到map中value的类型
fieldOIs.add(((MapObjectInspector)inputOI).getMapValueObjectInspector());
break;
default:
throw new UDFArgumentException("explode() takes an array or a map as a parameter");
}
//创建一个Struct类型返回
return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
} //输出list
private transient Object[] forwardListObj = new Object[1];
//输出map
private transient Object[] forwardMapObj = new Object[2]; /**
* 每行执行一次,输入数据args
* 每调用forward,输出一行
* @param args
* @throws HiveException
*/
@Override
public void process(Object[] args) throws HiveException {
switch (inputOI.getCategory()){
case LIST:
ListObjectInspector listOI = (ListObjectInspector) inputOI;
List<?> list = listOI.getList(args[0]);
if (list == null){
return;
} //list中每个元素输出一行
for (Object o: list){
forwardListObj[0] = o;
forward(forwardListObj);
}
break;
case MAP:
MapObjectInspector mapOI = (MapObjectInspector) inputOI;
Map<?, ?> map = mapOI.getMap(args[0]);
if (map == null){
return;
}
//map中每一对输出一行
for (Map.Entry<?, ?> entry: map.entrySet()){
forwardMapObj[0] = entry.getKey();
forwardMapObj[1] = entry.getValue();
forward(forwardMapObj);
}
break;
default:
throw new TaskExecutionException("explode() can only operate on an array or a map");
}
} @Override
public void close() throws HiveException { }
}
使用:
hive> add jar /root/udtf.jar
hive> create temporary function myexplode as 'udf.MyExplodeUDTF'
hive> select myexplode(tags) as tag from beauty
hive> select myexplode(props) as (k,v) from beauty
hive> select tag, count(tag) as cn from beauty lateral view myexplode(tags) lve_beauty as tag group by tag order by cn desc
【参考资料】
https://blog.csdn.net/wypersist/article/details/80314352
https://blog.csdn.net/zmywei_20160707/article/details/81698542
https://imcoder.site/article/detail?aid=131
Hive UDF函数构建的更多相关文章
- 如何编写自定义hive UDF函数
Hive可以允许用户编写自己定义的函数UDF,来在查询中使用.Hive中有3种UDF: UDF:操作单个数据行,产生单个数据行: UDAF:操作多个数据行,产生一个数据行. UDTF:操作一个数据行, ...
- hive UDF函数
虽然Hive提供了很多函数,但是有些还是难以满足我们的需求.因此Hive提供了自定义函数开发 自定义函数包括三种UDF.UADF.UDTF UDF(User-Defined-Function) ...
- Hadoop3集群搭建之——hive添加自定义函数UDF
上篇: Hadoop3集群搭建之——虚拟机安装 Hadoop3集群搭建之——安装hadoop,配置环境 Hadoop3集群搭建之——配置ntp服务 Hadoop3集群搭建之——hive安装 Hadoo ...
- hive下UDF函数的使用
1.编写函数 [java] view plaincopyprint?package com.example.hive.udf; import org.apache.hadoop.hive.ql. ...
- hive 中简单的udf函数编写
.注册函数,使用using jar方式在hdfs上引用udf库. $hive.注销函数,只需要删除mysql的hive数据记录即可. delete from func_ru ; delete from ...
- Hive中如何添加自定义UDF函数以及oozie中使用hive的自定义函数
操作步骤: 1. 修改.hiverc文件 在hive的conf文件夹下面,如果没有.hiverc文件,手工自己创建一个. 参照如下格式添加: add jar /usr/local/hive/exter ...
- Hive UDF 用户自定义函数 编程及使用
首先创建工程编写UDF 代码,示例如下: 1. 新建Maven项目 udf 本机Hadoop版本为2.7.7, Hive版本为1.2.2,所以选择对应版本的jar ,其它版本也不影响编译. 2. po ...
- hive自定义函数(UDF)
首先什么是UDF,UDF的全称为user-defined function,用户定义函数,为什么有它的存在呢?有的时候 你要写的查询无法轻松地使用Hive提供的内置函数来表示,通过写UDF,Hive就 ...
- Hive扩展功能(三)--使用UDF函数将Hive中的数据插入MySQL中
软件环境: linux系统: CentOS6.7 Hadoop版本: 2.6.5 zookeeper版本: 3.4.8 主机配置: 一共m1, m2, m3这五部机, 每部主机的用户名都为centos ...
随机推荐
- 上传docker镜像到阿里云镜像源
阿里云docker镜像配置 阿里云用户名可以使用淘宝系的,或者新注册都行. a. 配置阿里云的镜像加速器:加速器 然后在线上创建`镜像仓库`,需要设置`命名空间`和`仓库名称`,然后接着操作下面的步骤 ...
- Docker容器跨主机通信之:OVS+GRE
一.概述 由于docker自身还未支持跨主机容器通信,需要借助docker网络开源解决方案 OVS OpenVSwich即开放式虚拟交换机实现,简称OVS,OVS在云计算领域应用广泛,值得我们去学习使 ...
- Python与MogoDB交互
睡了大半天,终于有时间整理下拖欠的MongoDB的封装啦. 首先我们先进行下数据库的连接: conn = MongoClient('localhost',27017) # 建立连接 result = ...
- Tr/ee AtCoder - 4433 (构造)
大意: 给定长$n$的字符串$s$, 要求构造一棵树, 满足若第$i$个字符为$1$, 那么可以删一条边, 得到一个大小为$i$的连通块. 若为$0$则表示不存在一条边删去后得到大小为$i$的连通块. ...
- stone2 [期望]
也许更好的阅读体验 \(\mathcal{Description}\) 有 \(n\) 堆石子,依次编号为 \(1, 2,\ldots , n\),其中第 \(i\) 堆有 \(a_i\) 颗石子 你 ...
- ElasticSearch 429 Too Many Requests circuit_breaking_exception
错误提示 { "statusCode": 429, "error": "Too Many Requests", "message& ...
- yii框架定时任务的操作
在项目根目录里找到console(操作台,控制台)文件夹,在console文件夹里建一个TestController文件,如图所示: 文件内部写如下内容: 切记该文件继承的Controller一定是 ...
- 30个关于Shell脚本的经典案例(下)
本文目录 21.从FTP服务器下载文件 22.连续输入5个100以内的数字,统计和.最小和最大 23.将结果分别赋值给变量 24.批量修改文件名 25.统计当前目录中以.html结尾的文件总大 26. ...
- 科普帖:Linux操作系统
使用计算机必然会接触操作系统,现代操作系统已经发展的十分成熟,一般用户都可以很轻松的使用计算机.然而,对于要利用计算机进行专业开发和应用的用户来说,需要更加深入地理解操作系统的原理和运行机制,这样才能 ...
- aria2 https
https://github.com/aria2/aria2/issues/361 ... and also make sure that aria2 was built with HTTPS sup ...