概述
出于对可扩展性和性能的考虑,UDF已变成大数据生态圈查询引擎的必备功能之一,无论是Calcite、Hive、Impala都对其进行支持,但是UDF的支持有利也有弊,好处在于它提供了对某些用户独有需求的支持,例如某些产品需要将表中的某字段使用自定义的方式解析成可读字段,例如需要实现特殊的聚合函数;它的弊端在于它对用户开发,这样对于恶意的用户可能执行非正常的逻辑,例如在函数中删除或者拷贝其它文件内容,从而对非授权数据造成破坏,因此对于一个SQL引擎来说,我们需要UDF的集中管理,所有用户自定义的UDF都需要管理员审查源代码,不允许普通用户自己上传UDF,从而避免意外的发生。
对于通常UDF的需求,个人觉得有两方面的需求:1、系统提供的函数完成不了的需求,或者需要使用系统函数进行拼凑才能完成的需求。2、使用当前系统提供的函数性能太差,需要做一些特别的优化。另外,对于UDF还分为两类:自定义函数(UDF)和自定义聚合函数(UDAF),前者会处理每一条输入的记录,转换成处理后的结果,类似于map的功能,后者对于多条记录进行聚合,输出聚合之后的值,类似于reduce的功能。
众所周知Impala使用了Java和C++实现(虽然大多数时候我们都说Impala是C++实现的,所以性能更好,但是它的SQL解析部分的确是Java实现的),Impala同样也支持两种语言的UDF,但是UDAF目前只能支持C++实现,本文分别介绍这些方法如何在Impala中使用的。
目标
我们这里的example实现一个UDF和一个UDAF,分别实现如下的需求:
我们需要实现的UDF为int level(int),功能为根据value的值计算出距离该值最近的2的幂数的幂值,如果有多个值则取最大的。例如15,距离它最近的2的幂数为16,则level(15)=4,level(9)=3, level(12)=3或4则level(12)=4.
我们需要实现的UDAF为int sum_str(string),功能为计算name中出现的第一个整数的和值,如果该字符串不出现整数则为0,例如”abcd123ef”和”efdg23sd24”的和值为123+23=146.
这两个需求算是比较奇葩了吧,看看如何在impala中利用UDF和UDAF实现它们。
实现
Java版UDF
了解Impala的都知道,Impala可以直接使用Hive的metestore作为元数据库,同样Impala也可以直接使用Hive的UDF,所以对于之前奋斗在Hive第一线的同学们使用Impala有了不少亲切感,那么在这里就顺便温习一遍Hive的UDF使用流程吧。
使用Java实现的UDF必须继承org.apache.hadoop.hive.ql.exec.UDF类,然后在该类中实现名为evaluate的函数,猜测Hive/Impala是根据函数名来找到具体的实现的,当然一个类里面也可以重载多个evaluate方法。该方法实现如下:package com.netease.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
public class LevelUDF extends UDF {
public Integer evaluate(Integer value) {
if(value == null)
return null;
double temp = value;
int cnt = 0;
int max = 1;
while(temp > 1) {
cnt ++;
temp /= 2;
max *= 2;
}
if(max - value > (value - max / 2))
cnt --;
return cnt;
}
public static void main(String[] args) {
System.out.println(new Level