博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Hive UDAF介绍与开发
阅读量:5136 次
发布时间:2019-06-13

本文共 4712 字,大约阅读时间需要 15 分钟。

Hive UDAF介绍与开发


本文参考Hive社区wiki文档中UDAF而来。。采用Hive 1.2.1版本进行说明与测试。


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。

posted on
2016-01-17 18:48 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/Rudd/p/5137612.html

你可能感兴趣的文章
Package should contain a content type part [M1.13]异常的解决方式
查看>>
mask-image进度加载学习
查看>>
UITableView的优化
查看>>
jmeter关于中文乱码显示为问号的问题
查看>>
import pandas_datareader 报错“can't import name is_list_like”
查看>>
4. Median of Two Sorted Arrays
查看>>
Codeforces 785C Anton and Fairy Tale (规律+二分查找)
查看>>
Echarts学习求教
查看>>
PopupMenu介绍
查看>>
Log4j详细介绍(五)----输出地Appender
查看>>
几条高效的分页语句
查看>>
swift 初见-1
查看>>
javascript设计模式-工厂模式(简单工厂)
查看>>
离职之感
查看>>
python基础(二)
查看>>
AutoMapper 使用实践
查看>>
前端页面给指定的div添加遮罩层,并且带有加载中的小旋转图片
查看>>
条件(三目)运算符 (?:)
查看>>
XML基础
查看>>
相机位姿估计1_1:OpenCV:solvePnP二次封装与性能测试
查看>>