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

本文详细介绍了如何在Hive中开发和实现一个自定义用户定义聚合函数(UDAF),包括创建FieldLengthAggregationBuffer和FieldLengthUDAFEvaluator类,以及如何在Hive中注册和使用这些类。作者通过一步步的代码示例展示了开发过程和实际应用案例。
摘要由CSDN通过智能技术生成

开发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,符合预期:

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-k7zwfEuD-1714793809806)]

[外链图片转存中…(img-5MsgeJ5c-1714793809806)]

[外链图片转存中…(img-QVsPrPc3-1714793809807)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 18
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值