hive学习笔记之十:用户自定义聚合函数(UDAF)

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
开发UDAF分为以下几步:

  1. 新建类FieldLengthAggregationBuffer,用于保存中间结果,该类需继承AbstractAggregationBuffer;

  2. 新建类FieldLengthUDAFEvaluator,用于实现四个阶段中会被调用的方法,该类需继承GenericUDAFEvaluator;

  3. 新建类FieldLength,用于在hive中注册UDAF,里面会实例化FieldLengthUDAFEvaluator,该类需继承AbstractGenericUDAFResolver;

  4. 编译构建,得到jar;

  5. 在hive添加jar;

  6. 在hive注册函数;

接下来就按照上述步骤开始操作;

开发

  1. 打开前文新建的hiveudf工程,新建FieldLengthAggregationBuffer.java,这个类的作用是缓存中间计算结果,每次计算的结果都放入这里面,被传递给下个阶段,其成员变量value用来保存累加数据:

package com.bolingcavalry.hiveudf.udaf;

import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;

import org.apache.hadoop.hive.ql.util.JavaDataModel;

public class FieldLengthAggregationBuffer extends GenericUDAFEvaluator.AbstractAggregationBuffer {

private Integer value = 0;

public Integer getValue() {

return value;

}

public void setValue(Integer value) {

this.value = value;

}

public void add(int addValue) {

synchronized (value) {

value += addValue;

}

}

/**

  • 合并值缓冲区大小,这里是用来保存字符串长度,因此设为4byte

  • @return

*/

@Override

public int estimate() {

return JavaDataModel.PRIMITIVES1;

}

}

  1. 新建FieldLengthUDAFEvaluator.java,里面是整个UDAF逻辑实现,关键代码已经添加了注释,请结合前面的图片来理解,核心思路是iterate将当前分组的字段处理完毕,merger把分散的数据合并起来,再由terminate决定当前分组计算结果:

package com.bolingcavalry.hiveudf.udaf;

import org.apache.hadoop.hive.ql.metadata.HiveException;

import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;

import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;

import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;

import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;

/**

  • @Description: 这里是UDAF的实际处理类

  • @author: willzhao E-mail: zq2599@gmail.com

  • @date: 2020/11/4 9:57

*/

public class FieldLengthUDAFEvaluator extends GenericUDAFEvaluator {

PrimitiveObjectInspector inputOI;

ObjectInspector outputOI;

PrimitiveObjectInspector integerOI;

/**

  • 每个阶段都会被执行的方法,

  • 这里面主要是把每个阶段要用到的输入输出inspector好,其他方法被调用时就能直接使用了

  • @param m

  • @param parameters

  • @return

  • @throws HiveException

*/

@Override

public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {

super.init(m, parameters);

// COMPLETE或者PARTIAL1,输入的都是数据库的原始数据

if(Mode.PARTIAL1.equals(m) || Mode.COMPLETE.equals(m)) {

inputOI = (PrimitiveObjectInspector) parameters[0];

} else {

// PARTIAL2和FINAL阶段,都是基于前一个阶段init返回值作为parameters入参

integerOI = (PrimitiveObjectInspector) parameters[0];

}

outputOI = ObjectInspectorFactory.getReflectionObjectInspector(

Integer.class,

ObjectInspectorFactory.ObjectInspectorOptions.JAVA

);

// 给下一个阶段用的,即告诉下一个阶段,自己输出数据的类型

return outputOI;

}

public AggregationBuffer getNewAggregationBuffer() throws HiveException {

return new FieldLengthAggregationBuffer();

}

/**

  • 重置,将总数清理掉

  • @param agg

  • @throws HiveException

*/

public void reset(AggregationBuffer agg) throws HiveException {

((FieldLengthAggregationBuffer)agg).setValue(0);

}

/**

  • 不断被调用执行的方法,最终数据都保存在agg中

  • @param agg

  • @param parameters

  • @throws HiveException

*/

public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {

if(null==parameters || parameters.length<1) {

return;

}

Object javaObj = inputOI.getPrimitiveJavaObject(parameters[0]);

((FieldLengthAggregationBuffer)agg).add(String.valueOf(javaObj).length());

}

/**

  • group by的时候返回当前分组的最终结果

  • @param agg

  • @return

  • @throws HiveException

*/

public Object terminate(AggregationBuffer agg) throws HiveException {

return ((FieldLengthAggregationBuffer)agg).getValue();

}

/**

  • 当前阶段结束时执行的方法,返回的是部分聚合的结果(map、combiner)

  • @param agg

  • @return

  • @throws HiveException

*/

public Object terminatePartial(AggregationBuffer agg) throws HiveException {

return terminate(agg);

}

/**

  • 合并数据,将总长度加入到缓存对象中(combiner或reduce)

  • @param agg

  • @param partial

  • @throws HiveException

*/

public void merge(AggregationBuffer agg, Object partial) throws HiveException {

((FieldLengthAggregationBuffer) agg).add((Integer)integerOI.getPrimitiveJavaObject(partial));

}

}

  1. 最后是FieldLength.java,该类注册UDAF到hive时用到的,负责实例化FieldLengthUDAFEvaluator,给hive使用:

package com.bolingcavalry.hiveudf.udaf;

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.serde2.typeinfo.TypeInfo;

public class FieldLength extends AbstractGenericUDAFResolver {

@Override

public GenericUDAFEvaluator getEvaluator(GenericUDAFParameterInfo info) throws SemanticException {

return new FieldLengthUDAFEvaluator();

}

@Override

public GenericUDAFEvaluator getEvaluator(TypeInfo[] info) throws SemanticException {

return new FieldLengthUDAFEvaluator();

}

}

至此,编码完成,接下来是部署和体验;

部署和体验

本次部署的注册方式是临时函数,如果您想注册为永久函数,请参考前文;

  1. 在pom.xml所在目录执行mvn clean package -U,即可编译构建;

  2. 在target目录得到文件hiveudf-1.0-SNAPSHOT.jar;

  3. 上传到hive服务器,我这里是放在/home/hadoop/udf目录;

  4. 进入hive会话,执行以下命令添加jar:

add jar /home/hadoop/udf/hiveudf-1.0-SNAPSHOT.jar;

  1. 执行以下命令注册:

create temporary function udf_fieldlength as ‘com.bolingcavalry.hiveudf.udaf.FieldLength’;

  1. 找一个适合执行group by的表试试,我这里是前面的文章中创建的address表,完整数据如下:

hive> select * from address;

OK

1 guangdong guangzhou

2 guangdong shenzhen

3 shanxi xian

4 shanxi hanzhong

6 jiangshu nanjing

  1. 执行下面的SQL:

select province, count(city), udf_fieldlength(city) from address group by province;

执行结果如下,可见guangdong的guangzhou和shenzhen总长度为17,jiangsu的nanjing为7,shanxi的xian和hanzhong总长度12,符合预期:

最后

现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**

image
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
f_fieldlength(city) from address group by province;

执行结果如下,可见guangdong的guangzhou和shenzhen总长度为17,jiangsu的nanjing为7,shanxi的xian和hanzhong总长度12,符合预期:

最后

现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**

[外链图片转存中…(img-kc8kPig3-1714644106353)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值