1介绍

Hive自定义函数包括三种UDF、UDAF、UDTF

UDF(User-Defined-Function) 一进一出

UDAF(User- Defined Aggregation Funcation) 聚集函数,多进一出。Count/max/min

UDTF(User-Defined Table-Generating Functions)  一进多出,如lateral view explore)

使用方式 :在HIVE会话中add 自定义函数的jar文件,然后创建function继而使用函数

1、编写自定义函数

2、打包上传到集群机器中

3、进入hive客户端,添加jar包:hive> add jar /root/hive_udf.jar

4、创建临时函数:hive> create temporary function getLen as 'com.raphael.len.GetLength';

5、销毁临时函数:hive> DROP TEMPORARY FUNCTION getLen;

hive自定义函数中有几类数据类型:

PrimitiveObjectInspector  常用(基本数据类):

PrimitiveObjectInspector.PrimitiveCategory.STRING

ListObjectInspector

StructObjectInspector

MapObjectInspector

等类型

三种自定义函数都会首先进行参数个数和参数类型检查

参数类型检查(是那一种大类型(primitive...),然后具体是什么类型)

注意:在所有方法有返回时要注意返回的类型,最重要的是最后返回出去的结果,一般结果的数据类型会在初始化时就定义了,那么在最后返回结果是应该要转化成那种类型

初始化时init:

PrimitiveObjectInspector inputOI;

inputOI = (PrimitiveObjectInspector) parameters[0];

都会定义这种设置输入数据的 ObjectInspector(类型吧)

有多个参数就会设置多个。

在真正处理数据需要获取数据:

long distinctId = PrimitiveObjectInspectorUtils.getLong(objects[0], distinctIdOI);

一般都这样获取,objects是方法的参数,从外界传递进来的,包含很多值,获取第几个值,同时类型是什么(distinctIdOI)

2 UDF

2.1介绍

hive的udf有两种实现方式或者实现的API,一种是udf比较简单,一种是GenericUDF比较复杂。

如果所操作的数据类型都是基础数据类型,如(Hadoop&Hive 基本writable类型,如Text,IntWritable,LongWriable,DoubleWritable等等)。那么简单的org.apache.hadoop.hive.ql.exec.UDF就可以做到。

如果所操作的数据类型是内嵌数据结构,如Map,List和Set,那么要采用org.apache.hadoop.hive.ql.udf.generic.GenericUDF

2.2继承UDF实现

需要继承org.apache.hadoop.hive.ql.UDF,或者

org.apache.hadoop.hive.ql.udf.generic.GenericUDF,前者比较简单,只需要实现evaluate函数,evaluate函数支持重载。

UDF代码如下:

import org.apache.hadoop.hive.ql.exec.UDF;

public class GetLength extends UDF{

public int evaluate(String str) {

try{

return str.length();

}catch(Exception e){

return -1;

}

}

}

2.3继承GenericUDF实现

继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF需要实现三个方法:

1)initialize:只调用一次,在任何evaluate()调用之前可以接收到一个可以表示函数输入参数类型的object inspectors数组。initalize用来验证该函数是否接收正确的参数类型和参数个数,最后提供最后结果对应的数据类型。

2)evaluate:真正的逻辑,读取输入数据,处理数据,返回结果。

3)getDisplayString:返回描述该方法的字符串,没有太多作用。

继承GenericUDF实现UDF,完成url解码功能代码如下:

public class UrlDecodeUDF2 extends GenericUDF {

private transient PrimitiveObjectInspector inputOI;

public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {

// 检查参数数量

if (objectInspectors.length != 1) {

throw new UDFArgumentException("urlDecode() takes only one argument");

}

// 检查参数类型

if (objectInspectors[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {

if (((PrimitiveObjectInspector) objectInspectors[0]).getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.STRING) {

throw new UDFArgumentTypeException(0, "Only String type argument are accepted, but "

+ objectInspectors[0].getTypeName() + " was pass as parameter 1");

}

}

// 设置输入数据的 ObjectInspector

inputOI = (PrimitiveObjectInspector) objectInspectors[0];

// 输出数据的 ObjectInspector

return PrimitiveObjectInspectorFactory.writableStringObjectInspector;

}

public Object evaluate(DeferredObject[] deferredObjects) throws HiveException {

if (deferredObjects == null || deferredObjects[0] == null) {

return new Text("");

}

// 提取数据

String component = PrimitiveObjectInspectorUtils.getString(deferredObjects[0].get(), inputOI);

if (component == null || component.length() <= 0) {

return "";

}

String result = "";

//使用%25替换字符串中的%号

component = component.replaceAll("%(?![0-9a-fA-F]{2})", "%25");

try {

result = URLDecoder.decode(component, "UTF-8");

//            result = URLDecoder.decode(result, "UTF-8");

} catch (UnsupportedEncodingException e) {

result = component;

}

System.out.println(result);

return result;

}

public String getDisplayString(String[] strings) {

StringBuilder sb = new StringBuilder();

sb.append("url_decode");

sb.append("(");

if (strings.length > 0) {

sb.append(strings[0]);

for (int index = 1; index < strings.length - 1; index++) {

sb.append(",");

sb.append(strings[index]);

}

}

sb.append(")");

return sb.toString();

}

}

3 UDAF

3.1介绍

多行进一行出,如sum()、min(),用在group  by时。开发通用UDAF有两个步骤

1、第一个是编写resolver类(继承AbstractGenericUDAFResolver),

2、第二个是编写evaluator类(继承GenericUDAFEvaluator)在resolver类内部。

resolver负责类型检查,操作符重载。evaluator真正实现UDAF的逻辑。通常来说,顶层UDAF类继承org.apache.hadoop.hive.ql.udf.GenericUDAFResolver,里面编写嵌套类evaluator (继承GenericUDAFEvaluator)实现UDAF的逻辑。

实现evaluator所有evaluators必须继承抽象类

org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator。子类必须实现它的一些抽象方法,实现UDAF的逻辑。

同时需要一个继承了AggregationBuffer的类,来存储中间过程中记录的数据

3.2 Mode

GenericUDAFEvaluator有一个嵌套类Mode,这个类很重要,它表示了udaf在mapreduce的各个阶段,理解Mode的含义,就可以理解了hive的UDAF的运行流程。

PARTIAL1, //从原始数据到部分聚合数据的过程(map阶段),将调用iterate()和terminatePartial()方法。

PARTIAL2, //从部分聚合数据到部分聚合数据的过程(map端的combiner阶段),将调用merge() 和terminatePartial()方法。

FINAL,    //从部分聚合数据到全部聚合的过程(reduce阶段),将调用merge()和 terminate()方法。

COMPLETE  //从原始数据直接到全部聚合的过程(表示只有map,没有reduce,map端直接出结果),将调用merge() 和 terminate()方法。

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阶段,这个阶段直接输入原始数据,出结果。

3.3实现代码分析

UDAF的实现代码主干如下:

//最外层继承AbstractGenericUDAFResolver

public class GenericUDAFSum extends AbstractGenericUDAFResolver {

static final Log LOG = LogFactory.getLog(GenericUDAFSum.class.getName());

//实现getEvaluator方法

@Override

public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters)

throws SemanticException {

// Type-checking goes here!

return new GenericUDAFSumLong();

}

//编写一个类,继承GenericUDAFEvaluator,实现所有方法

public static class GenericUDAFSumLong extends GenericUDAFEvaluator {

// UDAF logic goes here!

}

}

说明:

1、getEvaluator:继承AbstractGenericUDAFResolver 所需要实现的方法,也只需要重写这一个方法,作用是检查参数个数,参数类型等,然后返回GenericUDAFEvaluator对象。

2、GenericUDAFEvaluator:getEvaluator最后返回类型就是这个类,真正返回(return)的是继承实现GenericUDAFEvaluator的类。

例如GenericUDAFSumLong 继承GenericUDAFEvaluator需要实现如下方法:

1、init(初始化):确定返回类型并返回UDAF的返回类型,

2、getNewAggregationBuffer:创建新的聚合计算的需要的内存,用来存储mapper,combiner,reducer运算过程中的相加总和,获取聚合中间结果缓存

3、reset:重置中间结果缓存

4、iterate: 迭代每一行的数据,等同于 Map 阶段。传进来的行数据由 HiveSQL 决定,计算的中间结果缓存到 AggregationBuffer ,@param aggregationBuffer 中间结果缓存,@param objects 行中每一列的数据

5、terminatePartial(终止部分):对部分中间结果数据进行合并,等同于 Map 阶段的 combine,返回mapper结果,combine后的结果

6、merge(合并):在最终进行 terminate() 前对所有传入的中间结果进行合并,等同于 Reduce 阶段的 merge。 各个 Map 传来的中间结果 partial 合并到 aggregationBuffer

7、terminate(终止):计算合并后的数据得出最终结果,等同于 Reduce 阶段的逻辑。reducer返回结果,或者是只有mapper,没有reducer时,在mapper端返回结果。

3.4代码示例

例子如下:

public class UDAFDemo extends  AbstractGenericUDAFResolver {

static final Log LOG = LogFactory.getLog(GenericUDAFSum.class.getName());

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.");

}

//        // 检查参数类型

//        if (parameters[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {

//            throw new UDFArgumentTypeException(0, "Only primitive type argument are accepted but "

//                    + parameters[0].getTypeName() + " was passed as parameter 1");

//        }

//        if (((PrimitiveTypeInfo) parameters[0]).getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.LONG) {

//            throw new UDFArgumentTypeException(0, "Only Long Type type argument 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 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 {

private PrimitiveObjectInspector inputOI;

private LongWritable result;

/** 存储sum的值的类 */

static class SumLongAgg implements AggregationBuffer {

boolean empty;

long sum;

}

//这个方法返回了UDAF的返回类型,这里确定了sum自定义函数的返回类型是Long类型

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;

}

//创建新的聚合计算的需要的内存,用来存储mapper,combiner,reducer运算过程中的相加总和

public AggregationBuffer getNewAggregationBuffer() throws HiveException {

SumLongAgg result = new SumLongAgg();

reset(result);

return result;

}

//mapreduce支持mapper和reducer的重用,所以为了兼容,也需要做内存的重用。

public void reset(AggregationBuffer aggregationBuffer) throws HiveException {

SumLongAgg myagg = (SumLongAgg) aggregationBuffer;

myagg.empty = true;

myagg.sum = 0;

}

private boolean warned = false;

//map阶段调用,只要把保存当前和的对象agg,再加上输入的参数,就可以了。

public void iterate(AggregationBuffer aggregationBuffer, Object[] objects) throws HiveException {

assert (objects.length == 1);

try {

merge(aggregationBuffer, objects[0]);

}catch (NumberFormatException e) {

if (!warned) {

warned = true;

LOG.warn(getClass().getSimpleName() + " "

+ StringUtils.stringifyException(e));

}

}

}

//mapper结束要返回的结果,还有combiner结束返回的结果

public Object terminatePartial(AggregationBuffer aggregationBuffer) throws HiveException {

return terminate(aggregationBuffer);

}

//combiner合并map返回的结果,还有reducer合并mapper或combiner返回的结果。

public void merge(AggregationBuffer aggregationBuffer, Object o) throws HiveException {

if (o != null) {

SumLongAgg agg = (SumLongAgg) aggregationBuffer;

agg.sum += PrimitiveObjectInspectorUtils.getLong(o, inputOI);

agg.empty = false;

}

}

//reducer返回结果,或者是只有mapper,没有reducer时,在mapper端返回结果。

public Object terminate(AggregationBuffer aggregationBuffer) throws HiveException {

SumLongAgg ragg = (SumLongAgg) aggregationBuffer;

if (ragg.empty) {

return null;

}

result.set(ragg.sum);

return result;

}

}

}

4 UDTF

4.1介绍

udtf用来解决输入一行输出多行(On-to-many maping) 的需求,需要继承org.apache.hadoop.hive.ql.udf.generic.GenericUDTF来实现三个方法。

1、initialize:返回UDTF的返回行的信息(返回个数,类型)。

2、process:真正的处理过程在process函数中,在process中,每一次forward()调用产生一行;如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。

3、close:对需要清理的方法进行清理。

forward()传入的就是最后的结果,里面一般是数组,数组有多少个元素就代码最后一行输出的结果有多少列

4.2代码示例

下面有一个切分”key:value;key:value;”这种字符串,返回结果为key, value两个字段。

public class UDTFDemo extends GenericUDTF {

@Override

public void close() throws HiveException {

// TODO Auto-generated method stub

}

/**

* 此方法返回UDTF的返回行的信息(返回个数,类型)

*/

@Override

public StructObjectInspector initialize(ObjectInspector[] args)

throws UDFArgumentException {

if (args.length != 1) {

throw new UDFArgumentLengthException("ExplodeMap takes only one argument");

}

if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE) {

throw new UDFArgumentException("ExplodeMap takes string as a parameter");

}

//多出的列名

ArrayList<String> fieldNames = new ArrayList<String>();

//每一列的类型

ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>();

fieldNames.add("c1");

fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);

fieldNames.add("c2");

fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);

return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames,fieldOIs);

}

/**

* 初始化完成之后会调用process方法,真正的处理过程在process函数中,在process中,每一次forward()调用产生一行;

* 如果产生多列可以将多个列的值放在一个数组中,然后将该数组传入到forward()函数。

*/

@Override

public void process(Object[] args) throws HiveException {

//每一行数 key:value,key:value,key:value

String input = args[0].toString();

String[] test = input.split(";");

for(int i=0; i<test.length; i++) {

try {

String[] result = test[i].split(":");

forward(result);

} catch (Exception e) {

continue;

}

}

}

}

5注意点(坑点)

1、使用String进行数据传递时,有时会出现String强转Hadoop的Text类型异常,或者Integer强转类型Text异常。

示例:自定义UDAF :

实现这个功能时:udaf的输出的list,类型是Object类型,最后统计时使用map存放计数map<object,integer>,注意最后填充到list中时要把map的key和value全部变成Text类型才行,不然可能会出现类型转换错误。我在iterate方法里面需要获取value时(此时类型是String),获取之后马上转换成Text之后存放的。

2、注意在一些稍微复杂的udaf中会使用list或者其他类型,在中间进行聚合等其他操作时,不同操作的传入的参数类型可能不同,最开始可能传递进来的数据是字符串类型或者Double类型的,但是经过一次迭代或者聚合之后变成了list,那么下一次或者下一个流程的传入数据的类型就变成了list,这时需要在初始化参数类型的时候定义好,如:

根据mode的流程判断处于哪一个阶段,参数类型就哪一种,就切换成哪一种,这里的返回值是定义的最终的返回结果。

hive自定义函数学习的更多相关文章

  1. Hive自定义函数的学习笔记(1)

    前言: hive本身提供了丰富的函数集, 有普通函数(求平方sqrt), 聚合函数(求和sum), 以及表生成函数(explode, json_tuple)等等. 但不是所有的业务需求都能涉及和覆盖到 ...

  2. hive -- 自定义函数和Transform

    hive -- 自定义函数和Transform UDF操作单行数据, UDAF:聚合函数,接受多行数据,并产生一个输出数据行 UDTF:操作单个数据 使用udf方法: 第一种: add jar xxx ...

  3. hive自定义函数(UDF)

    首先什么是UDF,UDF的全称为user-defined function,用户定义函数,为什么有它的存在呢?有的时候 你要写的查询无法轻松地使用Hive提供的内置函数来表示,通过写UDF,Hive就 ...

  4. hive自定义函数UDF UDTF UDAF

    Hive 自定义函数 UDF UDTF UDAF 1.UDF:用户定义(普通)函数,只对单行数值产生作用: UDF只能实现一进一出的操作. 定义udf 计算两个数最小值 public class Mi ...

  5. Hive 自定义函数(转)

    Hive是一种构建在Hadoop上的数据仓库,Hive把SQL查询转换为一系列在Hadoop集群中运行的MapReduce作业,是MapReduce更高层次的抽象,不用编写具体的MapReduce方法 ...

  6. Hive 自定义函数

    hive 支持自定义UDF,UDTF,UDAF函数 以自定义UDF为例: 使用一个名为evaluate的方法 package com.hive.custom; import org.apache.ha ...

  7. Hive 自定义函数 UDF UDAF UDTF

    1.UDF:用户定义(普通)函数,只对单行数值产生作用: 继承UDF类,添加方法 evaluate() /** * @function 自定义UDF统计最小值 * @author John * */ ...

  8. Hadoop之Hive自定义函数的陷阱

    A left join B, 这个B会连到A. 如<A1,B>, <A2,B>,在处理第一条记录的时候将B.clear(),则第二条记录的B是[]空的这是自定义UDF函数必须注 ...

  9. sql server自定义函数学习笔记

    sql server中函数分别有:表值函数.标量函数.聚合函数.系统函数.这些函数中除系统函数外其他函数都需要用户进行自定义. 一.表值函数 简单表值函数 创建 create function fu_ ...

随机推荐

  1. 字节顺序&字节对齐

    一.字节顺序的产生 在计算机中,数据是以字节为单位存放的,而c语言中只有char才是一个字节,其他如int,float都是大于一个字节,所以就存在将数据按怎样的顺序存放的问题.一般有大端序和小端序两种 ...

  2. IDEA光标类的操作

    1.Ctrl+Alt+Left/Right   光标定位到上一个/下一个浏览位置处: 2.Ctrl+Shift+Backspace   光标定位到上次修改的地方: 3.Alt+Up/Down   移动 ...

  3. fatal error: linux/videodev.h: No such file or directory

    Run Build Command:"/usr/bin/make" "cmTC_162a3/fast"/usr/bin/make -f CMakeFiles/c ...

  4. Mybatis出错: Cause: org.apache.ibatis.reflection.ReflectionException: Error instantiating class com.cyf.pojo.User with invalid types () or values ()

    org.apache.ibatis.exceptions.PersistenceException: ### Error querying database. Cause: org.apache.ib ...

  5. SpringCloud基础

    SpringCloud极大的简化了分布式系统的开发,实现了微服务的快速部署和灵活应用 SpringCloud主要框架 * 服务发现--Netfix Eureka * 服务调用--Netfix Feig ...

  6. sql 各种依赖关系解释

    1.数据依赖 数据依赖指的是通过一个关系中属性间的相等与否体现出来的数据间的相互关系,其中最重要的是函数依赖和多值依赖. 2.函数依赖 设X,Y是关系R的两个属性集合,当任何时刻R中的任意两个元组中的 ...

  7. SQL Server常用方法

    目录 CharIndex:确定某个字符的位置 Substring:截取 stuff: 根据位置替换字符串 replace:替换字符串 CharIndex:确定某个字符的位置 两个参数,前面是关键字,后 ...

  8. 〓经典文字MUD武侠游戏 我的江湖 〓

    〓经典文字MUD武侠游戏 我的江湖 〓 我的江湖(FFLIB)基于地狱内核扩展,目前已扩展了很多实用功能! 我的江湖玩法 和掌心西游.书剑.东方故事.侠缘.武林等玩法大同小异 但扩展了更多好玩的玩法, ...

  9. npm package

    lodash JavaScript 实用工具库,提供一致性,模块化,性能和配件等功能 用npm-run自动化任务 Express 4.x API 中文手册 Handlebars.js 模板引擎 使用n ...

  10. pidstat 命令详解

    pidstat 概述 pidstat是sysstat工具的一个命令,用于监控全部或指定进程的cpu.内存.线程.设备IO等系统资源的占用情况.pidstat首次运行时显示自系统启动开始的各项统计信息, ...