hive udaf总结

1 篇文章 0 订阅

0xx01 概述

hive的udaf全称 User-Defined Aggregation Functions。hive有两种udaf,simple and generic.区别如下

Simple UDAFs, as the name implies, are rather simple to write, but incur performance penalties because of the use of Java Reflection, and do not allow features such as variable-length argument lists. Generic UDAFs allow all these features, but are perhaps not quite as intuitive to write as Simple UDAFs.
简单 UDAF 因为使用Java反射导致性能损失,并且不允许使用变长参数列表等功能,已经被弃用了。通用的UDAF允许使用哪些特性,但是写起来不如简单的udaf直观。

所以以下的特性是基于generic udaf来说明的。

​ 开发自定义聚类函数涉及到两个抽象类

org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver
org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator

0xx02 抽象类介绍

​ 因为这两个都是抽象类的API,所以需要明确hive处理的流程,结合流程来进行理解。hive本质上是一个hdfs client,它将我们的sql转换成对应的mapreduce来进行执行,对应的mapreduce过程分别包含 maper,[combiner],reducer。

​ 所以udaf的执行应该是读取数据(mapper),根据设定(可能不存在)在reduce之前聚集一堆mapper计算部分结果(combiner),最终得到出一个最终结果(reducer)。因为在mapreduce模型中,符合分而治之然后汇总的思想,所以需要将不同机器上得到的结果进行聚集(如果combiner存在,我们需要将多个部分结果进行聚集,即使不存在也需要将多个mapper的结果进行聚集),所以需要保存部分聚集结果。

关于 AbstractGenericUDAFResolver

这个抽象类的作用是对输入的参数进行校验,需要被实现,然后重写以下方法

public GenericUDAFEvaluator getEvaluator(TypeInfo[] info) throws SemanticException {}

关于TypeInfo

TypeInfo[] 是对传入参数类型信息的抽象,支持八种类型
	1. Primitive objects (String, Number, etc)
 	2. List objects (a list of objects of a single type) 
 	3. Map objects (a map from objects of one type to objects of another type)
  4. Struct objects (a list of fields with names and their own types) 
  5. Union objects
  6. Decimal objects 
  7. Char objects 
  8. Varchar objects
通过它可以进行参数类别(getCategory())和 类型名称(getTypeName())以及限定类型名(getQualifiedName() 内部通过getTypeName()执行)的获取

返回值是一个GenericUDAFEvaluator的具体实现。

关于GenericUDAFEvaluator

​ udaf的具体处理逻辑需要在这里实现,需要实现该抽象类的如下方法

    public static class MyGeneric extends GenericUDAFEvaluator {
      
        //每个阶段都会执行init,不同阶段对应的parameters是不一样的,
        //在map阶段parameters代表的是sql语句中每个udaf对应参数的ObjectInspector,
        //而在combiner或者reducer中parameters代表部分聚合结果对应的ObjectInspector。所以要区分对待。
        //从iterate和merge的参数类型(一个数组类型,一个是object)就能看出来。
        @Override
        public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {
          //这行代码必须有
            super.init(m,parameters);
        }
      
      //创建新的聚合计算的需要的内存,用来存储mapper,combiner,reducer运算过程中的相加总和。
      // 保存数据聚集结果
        public AggregationBuffer getNewAggregationBuffer() throws HiveException {
            return null;
        }
      
      //mapreduce支持mapper和reducer的重用,所以为了兼容,也需要做内存的重用。
      // 重置聚集结果
				@Override
        public void reset(AggregationBuffer agg) throws HiveException {
        }
      
      //map阶段调用,迭代处理输入sql传过来的列数据,对每一列数据进行处理
				@Override
        public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {}
			// map与combiner结束返回结果,得到部分数据聚集结果	
      @Override
        public Object terminatePartial(AggregationBuffer agg) throws HiveException {
            return null;
        }
      
      //combiner合并map返回的结果,还有reducer合并mapper或combiner返回的结果。
				@Override
        public void merge(AggregationBuffer agg, Object partial) throws HiveException {
        }
      
      //reducer返回结果,或者是只有mapper,没有reducer时,在mapper端返回结果。
				@Override
        public Object terminate(AggregationBuffer agg) throws HiveException {
            return null;
        }
    }
Mode

因为需要根据mapReduce的不同阶段(map,combiner,reduce)进行不同的代码处理,这就涉及到GenericUDAFEvaluator的嵌套类Mode,这个类很重要,它表示了udaf在mapreduce的各个阶段,理解Mode的含义,就可以理解了hive的UDAF的运行流程。

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

​ 一般情况下,完整的UDAF逻辑是一个mapreduce过程,如果有mapper和reducer,就会经历PARTIAL1(mapper),FINAL(reducer),如果还有combiner,那就会经历PARTIAL1(mapper),PARTIAL2(combiner),FINAL(reducer)。而有一些情况下的mapreduce,只有mapper,而没有reducer,所以就会只有COMPLETE阶段,这个阶段直接输入原始数据,出结果。

objectInspector

同时还需要了解objectInspector接口,作用主要是解耦数据使用与数据格式,使得数据流在输入输出端切换不同的输入输出格式,不同的Operator上使用不同的格式。

图解Model和Evaluator关系
在这里插入图片描述

在这里插入图片描述

这里借用org.apache.hadoop.hive.ql.udf.generic.GenericUDAFSum的代码来进行分析

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

  }
备注:关于ObjectInspector(https://blog.csdn.net/czw698/article/details/38407817)

Hive 中operator处理的数据都是Object加上一个ObjectInspector对象,我们可以非常方便的通过该ObjectInspector对象了解到上游传过来的值是什么,如果是Struct对象,可以进一步了解到有多少了Filed,进而获取每个Filed的值,而且通过Serde可以方便的完成数据的序列化操作。

0xx03 总结

在 hive-exec对应的jar包中的org.apache.hadoop.hive.ql.exec.FunctionRegistry类描述了内置的udaf,如果想进阶学习,可以学习下这些代码

org.apache.hadoop.hive.ql.udf.generic.GenericUDAFAverage这里可以更详细的看到UDAF对hive运行流程的控制。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值