1.概念:
UDF: 用户定义函数
UDAF:用户定义聚合函数
UDTF:用户定义表生成函数
2.基本原理:
1.UDF: 针对一行数据,返回一个值
2.UDAF:针对多行数据,返回一个值,类似sum()、avg()
3.UDTF:一行数据可以化为多行输出。
3.UDF实现
很简单,继承UDF类就可以
/**
* @function 自定义UDF统计最小值
* @author ACER-
*
*/
public class Min extends UDF {
public Double evaluate(Double a, Double b) {
if (a == null)
a = 0.0;
if (b == null)
b = 0.0;
if (a >= b) {
return b;
} else {
return a;
}
}
}
4.UDAF实现:(难点)
1.实现方式有两种,简单和通用,更推荐通用,简单的UDAF已被弃用
2.通用UDAF涉及两个类,编写一个Resolver类,在编写一个Evaluate类。
3.Resolver负责类型检查,Evaluate负责真正的UDAF实现逻辑。
4.UDAF代码骨架:
public class GenericUDAFSum extends AbstractGenericUDAFResolver {
//用来写入警告和错误到Hive
static final Log LOG = LogFactory.getLog(GenericUDAFSum.class.getName());
//实现Resolver类
@Override
public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters)
throws SemanticException {
// Type-checking goes here!
return new GenericUDAFSumLong();
}
//实现Evaluator类
public static class GenericUDAFSumLong extends GenericUDAFEvaluator {
// UDAF logic goes here!
}
}
5.Resolver类具体实现类型检查:
只需要重写一个方法:getEvaluator
这里做了类型检查,如果不是原生类型(即符合类型,array,map此类),则抛出异常,
还实现了操作符重载,
对于整数类型,使用GenericUDAFSumLong实现UDAF的逻辑,
对于浮点类型,使用GenericUDAFSumDouble实现UDAF的逻辑。
public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters)
throws SemanticException {
if (parameters.length != 1) {
throw new UDFArgumentTypeException(parameters.length - 1,
"Exactly one argument is expected.");
}
if (parameters[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {
throw new UDFArgumentTypeException(0,
"Only primitive type arguments are accepted but "
+ parameters[0].getTypeName() + " is passed.");
}
switch (((PrimitiveTypeInfo) parameters[0]).getPrimitiveCategory()) {
case BYTE:
case SHORT:
case INT:
case LONG:
case TIMESTAMP:
return new GenericUDAFSumLong();
case FLOAT:
case DOUBLE:
case STRING:
return new GenericUDAFSumDouble();
case BOOLEAN:
default:
throw new UDFArgumentTypeException(0,
"Only numeric or string type arguments are accepted but "
+ parameters[0].getTypeName() + " is passed.");
}
6.实现Evaluator之前,UDAF在MapReduce中的各个阶段,在Mode类中体现。
1.UDAF中包含mapper和Reducer,则经历 PARTIAL1 -> FINAL
2.有Mapper、Combiner、Reducer,则PARTIAL1 -> PARTIAL2 -> FINAL
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
};
7.Evaluator类具体实现:
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;
}
}
参考:
http://www.cnblogs.com/ggjucheng/archive/2013/02/01/2888051.html
5.UDTF实现:(暂缓)
参考:
http://www.cnblogs.com/ggjucheng/archive/2013/02/01/2888819.html