上一篇我们讲解了基本UDF的编写,这一节我们来看下UDAF[User Defined Aggregation Functions],自定义聚合函数,用来处理输入多行,输出一行的操作,类似MapReduce中Reduce操作。UDAF是需要在hive的sql语句和group by联合使用,hive的group by对于每个分组,只能返回一条记录。
概述
Hive是构建在Hadoop上的数据仓库,我们的sql语句最终都是要变成Mapreduce
函数,只不过hive已经帮助我们写好并隐藏mapreduce
,给我们提供简洁的sql函数,所以我们要结合Mapper
、Combiner
与Reducer
来帮助我们理解UDAF函数。在hadoop集群中有若干台机器,在不同的机器上Mapper
与Reducer
任务独立运行。所以大体上来说,这个UDAF函数读取数据[mapper],聚集一堆mapper输出到部分聚集结果[combiner],并且最终创建一个最终的聚集结果[reducer]。因为我们跨域多个combiner进行聚集,所以我们需要保存部分聚集结果。在实现UDAF时候,Hive提供了两个重要的类org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver
和org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator
,我们先分析这两个类都做了什么工作,然后通过一个简单的例子来了解如何自定义UDAF。
UDAF重要的类及原理分析
AbstractGenericUDAFResolver
AbstractGenericUDAFResolver
很简单,要覆盖实现getEvaluator
方法,该方法会根据sql传递的参数数据格式指定调用哪个Evaluator进行处理。
public GenericUDAFEvaluator getEvaluator(TypeInfo[] info)
throws SemanticException {
throw new SemanticException(
"This UDAF does not support the deprecated getEvaluator() method.");
}
GenericUDAFEvaluator
所有Evaluator
必须继承抽象类org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator
。子类必须实现它的一些抽象方法,实现UDAF的逻辑。
ObjectInspector
ObjectInspector
作用主要是解耦数据使用与数据格式,使得数据流在输入输出端切换不同的输入输出格式,不同的Operator上使用不同的格式,可以通过这个抽象类知道上游传递过来的参数类型,从而解耦。一个ObjectInspector
对象本身并不包含任何数据,它只是提供***对数据的存储类型说明***和***对数据对象操作***的统一管理或者是代理。
public interface ObjectInspector extends Cloneable {
String getTypeName();
ObjectInspector.Category getCategory();
public static enum Category {
PRIMITIVE,
LIST,
MAP,
STRUCT,
UNION;
private Category() {
}
}
}
Model
Model
代表了UDAF在Mapreduce的各个阶段,定义如下:
public static enum Mode {
// PARTIAL1: from original data to partial aggregation data: iterate() and t
// erminatePartial() will be called.
PARTIAL1,
// PARTIAL2: from partial aggregation data to partial aggregation data:
// merge() and terminatePartial() will be called.
PARTIAL2,
// FINAL: from partial aggregation to full aggregation: merge() and
// terminate() will be called.
FINAL,
// COMPLETE: from original data directly to full aggregation: iterate() and
// terminate() will be called.
COMPLETE
};
我们来详细分析下各个阶段:
PARTIAL1
是map阶段,从原始数据到数据聚合,将会调用iterate
和terminatePartial
方法;PARTIAL2
相当于map阶段的Combine
,负责map端合并map的数据,将调用merge
和terminatePartial
方法;FINAL
相当于reduce阶段,从部分聚合到完全聚合,将会调用merge
和terminate
方法;COMPLETE
这个表示mapreduce中只有map没有reduce,直接在map端就得到结果了,从原始数据直接到完全聚合结果,将会调用iterate
和terminate
方法。
所以,完整的UDAF逻辑是一个mapreduce过程,如果有mapper和reducer,就会经历PARTIAL1(mapper),FINAL(reducer),如果还有combiner,那就会经历PARTIAL1(mapper),PARTIAL2(combiner),FINAL(reducer)。而有一些情况下只有mapper,而没有reducer,所以就会只有COMPLETE阶段,这个阶段直接输入原始数据,出结果。<