Hive-UDF、UDAF、UDTF

本文详细介绍了Hive中的三种自定义函数:用户定义函数(UDF)、用户定义聚合函数(UDAF)和用户定义表生成函数(UDTF)。重点讲解了UDAF的实现原理,包括Resolver和Evaluator类的使用,以及在MapReduce各阶段的具体表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值