《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
开发UDAF分为以下几步:
-
新建类FieldLengthAggregationBuffer,用于保存中间结果,该类需继承AbstractAggregationBuffer;
-
新建类FieldLengthUDAFEvaluator,用于实现四个阶段中会被调用的方法,该类需继承GenericUDAFEvaluator;
-
新建类FieldLength,用于在hive中注册UDAF,里面会实例化FieldLengthUDAFEvaluator,该类需继承AbstractGenericUDAFResolver;
-
编译构建,得到jar;
-
在hive添加jar;
-
在hive注册函数;
接下来就按照上述步骤开始操作;
开发
- 打开前文新建的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;
}
}
- 新建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));
}
}
- 最后是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();
}
}
至此,编码完成,接下来是部署和体验;
部署和体验
本次部署的注册方式是临时函数,如果您想注册为永久函数,请参考前文;
-
在pom.xml所在目录执行mvn clean package -U,即可编译构建;
-
在target目录得到文件hiveudf-1.0-SNAPSHOT.jar;
-
上传到hive服务器,我这里是放在/home/hadoop/udf目录;
-
进入hive会话,执行以下命令添加jar:
add jar /home/hadoop/udf/hiveudf-1.0-SNAPSHOT.jar;
- 执行以下命令注册:
create temporary function udf_fieldlength as ‘com.bolingcavalry.hiveudf.udaf.FieldLength’;
- 找一个适合执行group by的表试试,我这里是前面的文章中创建的address表,完整数据如下:
hive> select * from address;
OK
1 guangdong guangzhou
2 guangdong shenzhen
3 shanxi xian
4 shanxi hanzhong
6 jiangshu nanjing
- 执行下面的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等等**
《一线大厂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面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!