目录
一、UDAF 介绍
UDAF(User-Defined Aggregate Function)是用户自定义聚合函数的缩写。在大数据处理框架中,如 Apache Hive、Apache Flink、Apache Spark 等,UDAF 允许用户扩展内置聚合函数,以实现特定的数据处理需求。它们通常用于对一组数据进行计算,然后返回一个汇总结果。
UDAF 的主要特点:
1. 可定制性:用户可以根据自己的需求编写自定义聚合函数,实现特定的数据处理逻辑。
2. 灵活性:UDAF 可以在 SQL 查询中使用,类似于内置的聚合函数(如 SUM、AVG、COUNT 等)。
3. 可组合性:用户可以将 UDAF 与其他聚合函数或表达式组合使用,以实现更复杂的数据处理逻辑。
使用 UDAF 可以帮助用户更灵活地处理数据,满足特定的业务需求。然而,编写和维护 UDAF 也会带来额外的开发和调试成本,因此在使用 UDAF 之前,建议先评估内置聚合函数是否能满足需求。
创建 UDAF 的一般步骤:
1. 编写实现 UDAF 接口的类,定义所需的数据处理逻辑。
2. 编译并打包自定义 UDAF 类。
3. 将打包好的 UDAF 类添加到数据处理框架的类路径中。
4. 在 SQL 查询中使用自定义 UDAF 函数。
二、UDAF函数源码
row_number函数源码
@WindowFunctionDescription(
description = @Description(
name = "row_number",
value = "_FUNC_() - The ROW_NUMBER function assigns a unique number (sequentially, starting "
+ "from 1, as defined by ORDER BY) to each row within the partition."
),
supportsWindow = false,
pivotResult = true
)
public class GenericUDAFRowNumber extends AbstractGenericUDAFResolver {
static final Log LOG = LogFactory.getLog(GenericUDAFRowNumber.class.getName());
@Override
public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters) throws SemanticException {
if (parameters.length != 0) {
throw new UDFArgumentTypeException(parameters.length - 1, "No argument is expected.");
}
return new GenericUDAFRowNumberEvaluator();
}
static class RowNumberBuffer implements AggregationBuffer {
ArrayList<IntWritable> rowNums;
int nextRow;
void init() {
rowNums = new ArrayList<IntWritable>();
}
RowNumberBuffer() {
init();
nextRow = 1;
}
void incr() {
rowNums.add(new IntWritable(nextRow++));
}
}
public static class GenericUDAFRowNumberEvaluator extends GenericUDAFEvaluator {
@Override
public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {
super.init(m, parameters);
if (m != Mode.COMPLETE) {
throw new HiveException("Only COMPLETE mode supported for row_number function");
}
return ObjectInspectorFactory.getStandardListObjectInspector(
PrimitiveObjectInspectorFactory.writableIntObjectInspector);
}
@Override
public AggregationBuffer getNewAggregationBuffer() throws HiveException {
return new RowNumberBuffer();
}
@Override
public void reset(AggregationBuffer agg) throws HiveException {
((RowNumberBuffer) agg).init();
}
@Override
public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {
((RowNumberBuffer) agg).incr();
}
@Override
public Object terminatePartial(AggregationBuffer agg) throws HiveException {
throw new HiveException("terminatePartial not supported");
}
@Override
public void merge(AggregationBuffer agg, Object partial) throws HiveException {
throw new HiveException("merge not supported");
}
@Override
public Object terminate(AggregationBuffer agg) throws HiveException {
return ((RowNumberBuffer) agg).rowNums;
}
}
}
三、UDAF详解
UDAF开发主要涉及到以下两个抽象类:
org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver
org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator
AbstractGenericUDAFResolver
Resolver很简单,要覆盖实现下面方法,该方法会根据sql传入的参数数据格式指定调用哪个Evaluator进行处理。
GenericUDAFEvaluator
UDAF逻辑处理主要发生在Evaluator中,要实现该抽象类的几个方法。
// 确定各个阶段输入输出参数的数据格式ObjectInspectors
public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException;
// 保存数据聚集结果的类
abstract AggregationBuffer getNewAggregationBuffer() throws HiveException;
// 重置聚集结果
public void reset(AggregationBuffer agg) throws HiveException;
// map阶段,迭代处理输入sql传过来的列数据
public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException;
// map与combiner结束返回结果,得到部分数据聚集结果
public Object terminatePartial(AggregationBuffer agg) throws HiveException;
// combiner合并map返回的结果,还有reducer合并mapper或combiner返回的结果。
public void merge(AggregationBuffer agg, Object partial) throws HiveException;
// reducer阶段,输出最终结果
public Object terminate(AggregationBuffer agg) throws HiveException;
ObjectInspector
作用主要是解耦数据使用与数据格式,使得数据流在输入输出端切换不同的输入输出格式,不同的Operator上使用不同的格式。可以参考这两篇文章:first post on Hive UDFs、Hive中ObjectInspector的作用,里面有关于objectinspector的介绍。
Model
Model代表了UDAF在mapreduce的各个阶段。
public static enum Mode {
/**
* PARTIAL1: 这个是mapreduce的map阶段:从原始数据到部分数据聚合
* 将会调用iterate()和terminatePartial()
*/
PARTIAL1,
/**
* PARTIAL2: 这个是mapreduce的map端的Combiner阶段,负责在map端合并map的数据::从部分数据聚合到部分数据聚合:
* 将会调用merge() 和 terminatePartial()
*/
PARTIAL2,
/**
* FINAL: mapreduce的reduce阶段:从部分数据的聚合到完全聚合
* 将会调用merge()和terminate()
*/
FINAL,
/**
* COMPLETE: 如果出现了这个阶段,表示mapreduce只有map,没有reduce,所以map端就直接出结果了:从原始数据直接到完全聚合
* 将会调用 iterate()和terminate()
*/
COMPLETE
};
图解Model与Evaluator关系
Model各阶段对应Evaluator方法调用
一般情况下,完整的UDAF逻辑是一个mapreduce过程,如果有mapper和reducer,就会经历PARTIAL1(mapper),FINAL(reducer),如果还有combiner,那就会经历PARTIAL1(mapper),PARTIAL2(combiner),FINAL(reducer)。而有一些情况下的mapreduce,只有mapper,而没有reducer,所以就会只有COMPLETE阶段,这个阶段直接输入原始数据出结果。
四、实现自定义 UDAF
实现求和样例:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hive.common.type.HiveDecimal;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.parse.WindowingSpec.BoundarySpec;
import org.apache.hadoop.hive.ql.plan.ptf.BoundaryDef;
import org.apache.hadoop.hive.ql.plan.ptf.WindowFrameDef;
import org.apache.hadoop.hive.ql.util.JavaDataModel;
import org.apache.hadoop.hive.serde2.io.DoubleWritable;
import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorUtils;
import org.apache.hadoop.hive.serde2.typeinfo.DecimalTypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.PrimitiveTypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfoFactory;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.util.StringUtils;
public static class GenericUDAFSumLong extends GenericUDAFEvaluator {
private PrimitiveObjectInspector inputOI;
private LongWritable result;
//这个方法返回了UDAF的返回类型,这里确定了sum自定义函数的返回类型是Long类型
@Override
public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {
assert (parameters.length == 1);
super.init(m, parameters);
result = new LongWritable(0);
inputOI = (PrimitiveObjectInspector) parameters[0];
return PrimitiveObjectInspectorFactory.writableLongObjectInspector;
}
/** 存储sum的值的类 */
static class SumLongAgg implements AggregationBuffer {
boolean empty;
long sum;
}
//创建新的聚合计算的需要的内存,用来存储mapper,combiner,reducer运算过程中的相加总和。
@Override
public AggregationBuffer getNewAggregationBuffer() throws HiveException {
SumLongAgg result = new SumLongAgg();
reset(result);
return result;
}
//mapreduce支持mapper和reducer的重用,所以为了兼容,也需要做内存的重用。
@Override
public void reset(AggregationBuffer agg) throws HiveException {
SumLongAgg myagg = (SumLongAgg) agg;
myagg.empty = true;
myagg.sum = 0;
}
private boolean warned = false;
//map阶段调用,只要把保存当前和的对象agg,再加上输入的参数,就可以了。
@Override
public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {
assert (parameters.length == 1);
try {
merge(agg, parameters[0]);
} catch (NumberFormatException e) {
if (!warned) {
warned = true;
LOG.warn(getClass().getSimpleName() + " "
+ StringUtils.stringifyException(e));
}
}
}
//mapper结束要返回的结果,还有combiner结束返回的结果
@Override
public Object terminatePartial(AggregationBuffer agg) throws HiveException {
return terminate(agg);
}
//combiner合并map返回的结果,还有reducer合并mapper或combiner返回的结果。
@Override
public void merge(AggregationBuffer agg, Object partial) throws HiveException {
if (partial != null) {
SumLongAgg myagg = (SumLongAgg) agg;
myagg.sum += PrimitiveObjectInspectorUtils.getLong(partial, inputOI);
myagg.empty = false;
}
}
//reducer返回结果,或者是只有mapper,没有reducer时,在mapper端返回结果。
@Override
public Object terminate(AggregationBuffer agg) throws HiveException {
SumLongAgg myagg = (SumLongAgg) agg;
if (myagg.empty) {
return null;
}
result.set(myagg.sum);
return result;
}
}
参考资料:
hive官方wiki:LanguageManual UDF - Apache Hive - Apache Software Foundation