用户自定义方法(函数)
本文为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
自己看看代码吧,有注释:
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!!!