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

来源:互联网 发布:重庆网站外包优化 编辑:程序博客网 时间:2024/05/29 15:26

用户自定义方法(函数)

本文为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!!!

0 0
原创粉丝点击