zoukankan      html  css  js  c++  java
  • Hive UDAF介绍与开发

    UDAF简介

    UDAF是用户自定义聚合函数。Hive支持其用户自行开发聚合函数完成业务逻辑。

    通俗点说,就是你可能需要做一些特殊的甚至是非常扭曲的逻辑聚合,但是Hive自带的聚合函数不够玩,同时也还找不到高效的等价玩法,那么,这时候就该自己写一个UDAF了。

    而从实现上来看,Hive的UDAF分为两种:

    • Simple。即继承org.apache.hadoop.hive.ql.exec.UDAF类,并在派生类中以静态内部类的方式实现org.apache.hadoop.hive.ql.exec.UDAFEvaluator接口。这种方式简单直接,但是在使用过程中需要依赖JAVA反射机制,因此性能相对较低。在Hive源码包org.apache.hadoop.hive.contrib.udaf.example中包含几个示例。可以直接参阅。但是这些接口已经被注解为Deprecated,建议不要使用这种方式开发新的UDAF函数。
    • Generic。这是Hive社区推荐的新的写法,以抽象类代替原有的接口。新的抽象类org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver替代老的UDAF接口,新的抽象类org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator替代老的UDAFEvaluator接口。

    产生这两种方式的原因并不高深,就是结构演进,历史遗留。原文链接最后一段说明了一下演进的版本以及原因。

    UDAF相关类和接口简介

    • AbstractGenericUDAFResolver:该抽象类实现了GenericUDAFResolver2的接口。UDAF主类须继承该抽象类,其主要作用是实现参数类型检查和操作符重载。可以为同一个函数实现不同入参的版本。
    • org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator:该抽象类为UDAF具体的逻辑处理,包括几个必须实现的抽象方法,这几个方法负责完成UDAF所需要处理的逻辑。

    UDAF的运行流程简介

    抽象类GenericUDAFEvaluator中,包含一个静态内部枚举类,和一系列抽象方法。这个枚举类的注释中,解释了各个枚举值的运行阶段和运行内容。按照时间先后顺序,分别有:

    • PARTIAL1:原始数据到部分聚合,调用iterate和terminatePartial --> map阶段
    • PARTIAL2: 部分聚合到部分聚合,调用merge和terminatePartial --> combine阶段
    • FINAL: 部分聚合到完全聚合,调用merge和terminate --> reduce阶段
    • COMPLETE: 从原始数据直接到完全聚合 --> map阶段,并且没有reduce

    那么,这几个方法分别干了些啥呢?

    • init: 实例化Evaluator类的时候调用的,在不同的阶段需要返回不同的OI。其入参和返回值,以及Mode阶段的关系如下表:

       入参返回值的使用者
      P1 原始数据 terminatePartial
      P2 部分聚合数据 terminatePartial
      F 部分聚合数据 terminate
      C 原始数据 terminate
    • getNewAggregationBuffer: 获取存放中间结果的对象
    • iterate:处理一行数据
    • terminatePartial:返回部分聚合数据的持久化对象。因为调用这个方法时,说明已经是map或者combine的结束了,必须将数据持久化以后交给reduce进行处理。只支持JAVA原始数据类型及其封装类型、HADOOP Writable类型、List、Map,不能返回自定义的类,即使实现了Serializable也不行,否则会出现问题或者错误的结果。
    • merge:将terminatePartial返回的部分聚合数据进行合并,需要使用到对应的OI。
    • terminate:结束,生成最终结果。

    两类UDAF基本原理相同,下面以histogram_numeric这个系统自带的Generic UDAF为例,描述一下UDAF的运行和开发过程。这个函数涵盖了UDAF多个特性,比如入参类型检查并返回复杂数据类型。

    UDAF开发

    1. 构造UDAF代码骨架部分

    先搭建好代码骨架,完成需要继承的类和接口结构。

    public class GenericUDAFHistogramNumeric extends AbstractGenericUDAFResolver {
      static final Log LOG = LogFactory.getLog(GenericUDAFHistogramNumeric.class.getName());
     
      @Override
      public GenericUDAFEvaluator getEvaluator(GenericUDAFParameterInfo info) throws SemanticException {
        // TODO: 1. Type-checking goes here!
     
        return new GenericUDAFHistogramNumericEvaluator();
      }
     
      public static class GenericUDAFHistogramNumericEvaluator extends GenericUDAFEvaluator {
        // UDAF logic goes here!
      }
    }

    2.实现getEvaluator方法

    该方法非常简单,其主要目的是校验UDAF的入参个数和入参类型并返回Evaluator对象。调用者传入不同的参数时,向其返回不同的Evaluator或者直接抛出异常。这部分代码可以写入骨架代码中的TODO:1处。例如本例中的实现,该UDAF不支持多种参数的版本,限定参数个数必须为2,并且第一个参数必须是简单数据类型,第二个参数必须是int。

      @Override
      public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters) throws SemanticException {
        if (parameters.length != 2) {
          throw new UDFArgumentTypeException(parameters.length - 1,
              "Please specify exactly two arguments.");
        }
    
        // validate the first parameter, which is the expression to compute over
        if (parameters[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {
          throw new UDFArgumentTypeException(0,
              "Only primitive type arguments are accepted but "
              + parameters[0].getTypeName() + " was passed as parameter 1.");
        }
        switch (((PrimitiveTypeInfo) parameters[0]).getPrimitiveCategory()) {
        case BYTE:
        case SHORT:
        case INT:
        case LONG:
        case FLOAT:
        case DOUBLE:
        case TIMESTAMP:
        case DECIMAL:
          break;
        case STRING:
        case BOOLEAN:
        case DATE:
        default:
          throw new UDFArgumentTypeException(0,
              "Only numeric type arguments are accepted but "
              + parameters[0].getTypeName() + " was passed as parameter 1.");
        }
    
        // validate the second parameter, which is the number of histogram bins
        if (parameters[1].getCategory() != ObjectInspector.Category.PRIMITIVE) {
          throw new UDFArgumentTypeException(1,
              "Only primitive type arguments are accepted but "
              + parameters[1].getTypeName() + " was passed as parameter 2.");
        }
        if( ((PrimitiveTypeInfo) parameters[1]).getPrimitiveCategory()
            != PrimitiveObjectInspector.PrimitiveCategory.INT) {
          throw new UDFArgumentTypeException(1,
              "Only an integer argument is accepted as parameter 2, but "
              + parameters[1].getTypeName() + " was passed instead.");
        }
    
        return new GenericUDAFHistogramNumericEvaluator();
      }
    

    3.实现Evaluator

    从骨架代码中,可以看到一个静态内部类实现了Evaluator的抽象类,并且必须实现它的几个抽象方法。这些方法的调用时机即意义参见上面的表格以及GenericUDAFEvaluator类的静态内部枚举类Mode

    4. 注册函数

    将函数直接写入FunctionRegistry类的静态代码块中,system.registerGenericUDAF("histogram_numeric", new GenericUDAFHistogramNumeric());,或者将UDAF代码单独打包成jar,采用CREATE FUNCTION语句创建函数。

    End。

    

  • 相关阅读:
    part11-1 Python图形界面编程(Python GUI库介绍、Tkinter 组件介绍、布局管理器、事件处理)
    part10-3 Python常见模块(正则表达式)
    Cyclic Nacklace HDU
    模拟题 Right turn SCU
    状态DP Doing Homework HDU
    Dp Milking Time POJ
    区间DP Treats for the Cows POJ
    DP Help Jimmy POJ
    Dales and Hills Gym
    Kids and Prizes Gym
  • 原文地址:https://www.cnblogs.com/itxuexiwang/p/6264547.html
Copyright © 2011-2022 走看看