Hive 9. 用户定义的方法(UDF-User-Defined Functions)

用户自定义方法(函数)

本文为Hadoop 权威指南第四版(英文原版),Hive 章节的部分翻译。仅限交流使用。

Henvealf/译

有时候 Hive 提供的函数并不能满足我们的需求,Hive 允许用户自己自定义(UDF),可插拔的加入到 Hive 中去使用。

主要是用 Java 写,也可以使用其他语言,就是使用流的来实现,这里不做讨论。

UDF 一共有三种类型:UDF(User-Defined Function), UDFA(User-Defined aggregate function,用户自定义的聚合函数 ),UDTF(User-Defined table-generating function,用户自定义的生成表的函数) 。

  • UDF 是操作一行而生成一行数据。

  • UDFA 是操作多行而最终生成一行 (也就是所谓的聚合函数)。

  • UDTA 是操作一行而生成多行(也就是生成表)。

表生成函数确实是很少听过,他在 Hive 中主要是以集合类型(例如Array)作为参数的函数,比如将一个数组中的每个值取出,每个值各自单独成一列,即是一列变多列,容易理解,就不举例子了。

写一个 UDF

有一个小栗子,写一个函数,功能与 trim 类似,去除字符串开始与结束的空格。这里我们还可以指定要去除的字符串。

packge com.henvealf;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;

public class Strip extends UDF {
    private Text result = new Text();

    public Text evaluate(Text str) {
        if(str == null) {
            return null;
        }
        result.set(StringUtils.strip(str.toString()));
        return result;
    }

    public Text evaluate(Text str, String stripChars) {
        if(str == null) {
            return null;
        }
        result.set(StringUtils.strip(str.toString(),stripChars));
        return result;
    }
}

UDF 的编写必须符合下面两个条件:

  • 必须是 org.apache.hadoop.hive.ql.exec.UDF 的子类;
  • 至少实现一个 evaluate() 方法。

    解释: evaluater 求值程序

    evaluate() 方法并没有使用接口来定义,因为他需要有任意数量和任意类型的参数,并能够返回任意类型的返回值。Hive在函数被调用时通过反射来寻找要被调用的方法。

Hive 中操作 UDF

创建一个方法:

Create Function strip 'com.henvealf.Strip'
Using Jar '/user/henvealf/udf/udf.jar'

创建一个临时方法,其只在当前会话中生效:

Add Jar /user/henvealf/udf/udf.jar;
Create Temporary Function strip 'com.henvealf.Strip'

删除方法:

Drop Function strip;

注意,如果 Hive 在分布模式下,这里的路径就是 DFS 的路径,否则就是本地路径。

编写 UDAF

UDAF(聚合函数) 比 UDF 复杂许多。下面这个有一个求最大值的例子:

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


public class Maximum extends UADF {

    public static class MaximumIntUDAFEvaluator implements UDAFEvaluator {

        private IntWritable result;

        public void init() {
            result = null;
        }

        public boolean iterate (IntWritable value) {
            if(value == null) {
                return  true;
            }
            if(result == null) {
                result = new IntWritable(value.get());
            } else {
                result.set(Max.max(result.get(), value.get()));
            }

            return true;
        }


        public IntWritable terminatePartial() {
            return result;
        }

        public boolean merge(IntWritable other) {
            return iterate(other);
        }

        public IntWritable terminate() {
            return result;
        }
    }

}

UDAF 和 UDF 相比较,可以发现其 Evaluator 由一个方法升级成了一个类,且这个类必须实现 org.apache.hadoop.hive.ql.exec.UDAFEvaluator 接口, 接口中有需要实现的抽象许多方法,也就是说 Hive 调用该聚合函数的时候是联合这些方法来完成函数任务,这里介绍一下:

  • init()

    init() 用于初始化每个求值器(Evaluator),以及重新设置他的状态。因为一个聚合函数中有许多的 Evaluator,在不同的时刻或并发,或顺序工作。在本例中,将记录最终结果用的 result 设置为空,这里的空(null)对应数据库中的 NULL.

  • iterate()

    iterate() 每被调用一次,都会有一个新的值被聚合。这时求值器就应该有一个内置的标识(这里就是 result)来记录并随时更新值。iterate() 的参数类型要与函数的输入格式相同。返回值为 true 意味着输入值可用。

  • terminatePartial()

    负责对每个局部聚合的结果返回一个值。这个方法必须返回一个封装了标志的聚合体对象(这里是 result )。

  • merge()

    这个方法在每个局部聚合求值器执行完成后调用(和 reducer 很像啊),用来合并他们的输出,这个方法得到一个对象,且必须与 terminatePartial() 的返回值相同。

  • terminate()

    这个方法在所有的工作完成后做收尾,做最后的聚合并返回最后结果。

hive> Create Temparary Function maximum As 'com.henvealf.Maximum'
hive> Select * maximum(temperature) From record;

看下图:

UDAF过程

一个复杂点的 UDAF

自己看看代码吧,有注释:

public class Mean extends UDAF {

    public static class PartialResult {
        double sum;
        int count;
    }

    public static class MeanUDAFEvaluator implements UDAFEvaluator {

        private PartialResult partialResult;

        // 初始化,实例化字段partialResult;
        // 对每个局部输入调用一次。
        // 每个 merge 开始时候调用一次。
        public void init() {
            partialResult = new ParagraphView();
        }

        // 对每个文件的double 求和,并计数。会被调用多次。
        public boolean iterate( DoubleWritable inputValue ) {
            if ( inputValue == null ) {
                return true;
            }
            partialResult.sum += inputValue.get();
            partialResult.count ++;

            return true;
        }

        // 终止局部处理后的操作,直接返回 partialResult。
        // iterate 处理完一个局部输入后调用。
        public PartialResult terminatePartial() {
            return partialResult;
        }

        // 合并各个局部操作的结果
        // 有几个局部输出就调用几次。
        public boolean merge(  PartialResult other ) {
            if( other == null ) {
                return true;
            }
            partialResult.sum += other.sum;
            partialResult.count += other.count;

            return true;
        }


        // 在最最最后调用。
        public DoubleWritable terminate() {
            return new DoubleWritable(partialResult.sum / partialResult.count);
        }

    }

}

End!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值