Hive UDAF开发指南

目录

一、UDAF 介绍

二、UDAF函数源码

三、UDAF详解

四、实现自定义 UDAF


一、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 UDFsHive中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

博客:hive:UDAF开发详解_hive udaf_花和尚也有春天的博客-CSDN博客

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值