hive-基础-UDAF概述

UDF

@Description(name = "Upper()",
        value = "_FUNC_(originArray,trimStr) " +
                "Remove the trimStr value of originArray .  Returns NULL if originArray is NULL." +
                "Returns null if elementStr is not found in originArray or is NULL."
)
public class Upper extends UDF {
    public String evaluate(String str1, String str2) {
        try {
            return str1.toUpperCase()+"&&"+str2;
        } catch (Exception e) {
            return null;
        }
    }
}

然后执行

add jar /home/sean/workspace/zsh-udf/zsh-demo-udf.jar;
create temporary function upper as 'Upper';
select upper(t.col1) from t limit 10;
drop temporary function upper;
delete jar /home/sean/workspace/zsh-udf/zsh-demo-udf.jar;

UDAF

UDAF和group by一起使用
resolver:resolver负责类型检查,操作符重载
evaluator:UDAF逻辑

resolver(继承AbstractGenericUDAFResolver)
大体框架

public class GenericUDAFSum extends AbstractGenericUDAFResolver {
  static final Log LOG = LogFactory.getLog(GenericUDAFSum.class.getName());

  @Override
  public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters)
    throws SemanticException {
    // Type-checking goes here!
    return new GenericUDAFSumLong(); 
  } 

  public static class GenericUDAFSumLong extends GenericUDAFEvaluator {
    // UDAF logic goes here!
  } 
}

第一行创建LOG对象,用来写入警告和错误到hive的log;
继承AbstractGenericUDAFResolver之后,只需要重写getEvaluator,根据SQL传入的参数返回正确的evaluator,这里是为了实现运算符的重载;

getEvaluator的完整代码如下,这个方法返回的一堆就是后面要实现的

public class GenericUDAFSum extends AbstractGenericUDAFResolver {
    @Override
    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.");
    }

    public static class GenericUDAFSumLong extends GenericUDAFEvaluator {
		...
    }
}

如果不是原生类型(即符合类型,array,map此类),则抛出异常;
还实现了操作符重载,对于整数类型,使用GenericUDAFSumLong实现UDAF的逻辑;
对于浮点类型,使用GenericUDAFSumDouble实现UDAF的逻辑。
这里一共返回:GenericUDAFSumLong和GenericUDAFSumDouble这两种情况,这两种GenericUDAFSumEvaluator;

evaluator
继承抽象类GenericUDAFEvaluator,内部类Mode如下

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()和
      * ()
     */
    COMPLETE
  };

evaluator必须实现的方法

1、getNewAggregationBuffer():返回存储临时聚合结果的AggregationBuffer对象。
2、reset(AggregationBuffer agg):重置聚合结果对象,以支持mapper和reducer的重用。
3、iterate(AggregationBuffer agg,Object[] parameters):迭代处理原始数据parameters并保存到agg中。
4、terminatePartial(AggregationBuffer agg):以持久化的方式返回agg表示的部分聚合结果,这里的持久化意味着返回值只能Java基础类型、数组、基础类型包装器、Hadoop的Writables、Lists和Maps。
5、merge(AggregationBuffer agg,Object partial):合并由partial表示的部分聚合结果到agg中。
6、terminate(AggregationBuffer agg):返回最终结果。

还可以覆盖初始化方法

ObjectInspector init(Mode m,ObjectInspector[] parameters)

不同模式下parameters含义不同

#parameters的作用
PARTIAL1、COMPLETE ---> parameters为原始数据
PARTIAL2、FINAL ---> parameters为部分聚合数据,只有一个元素

#ObjectInspector的作用
PARTIAL1、PARTIAL2 --> ObjectInspector用于terminatePartial方法的返回值
FINAL、COMPLETE ---> ObjectInspector 用于terminate方法的返回值

实现UDAF是一个完整的mapreduce过程

有mapper和reducer ---> 会经历PARTIAL1(mapper),FINAL(reducer)过程
有combiner ---> 经历PARTIAL1(mapper),PARTIAL2(combiner),FINAL(reducer)
只有map没有reduce ---> 只有COMPLETE (直接输入原始数据出结果)

GenericUDAFSumLong的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;
    }

  }

需要继续阅读:GenericUDAFAverage

posted @ 2016-12-09 13:16  zhangshihai1232  阅读(374)  评论(1)    收藏  举报