曾经认为Hive自带的函数已经足够满足日常需求,未曾料到,还是会遇到需要自定义函数的情况。
需求非常明确:我需要模拟Oracle中的SYS_GUID()函数,生成一个32位的字母数字随机串。
以下是实现这一需求的代码:
```java import java.util.Random; import org.apache.hadoop.hive.ql.exec.UDF; import org.apache.hadoop.hive.ql.udf.UDFType; import org.apache.hadoop.io.Text;
@UDFType(deterministic = false) public class HiveUDFOracleSysguid extends UDF { private Random random = new Random(); private Text result = new Text();
public Text evaluate(int length) {
result.set(getCharAndNumr(length));
return result;
}
private String getCharAndNumr(int length) {
StringBuffer val = new StringBuffer();
for (int i = 0; i < length; i++) {
String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
if ("char".equalsIgnoreCase(charOrNum)) {
int choice = 65;
val.append((char) (choice + random.nextInt(26)));
} else if ("num".equalsIgnoreCase(charOrNum)) {
val.append(String.valueOf(random.nextInt(10)));
}
}
return val.toString();
}
} ```
步骤一:将该类打包,并上传至集群。
步骤二:将jar包添加到Hive的classpath中:
sql
hive> add jar /home/hive-tools-sysguid-for-oracle.jar;
步骤三:基于自定义UDF类创建自定义函数:
sql
hive> create temporary function sys_guid as 'cn.bigdata.tools.HiveUDFOracleSysguid';
其中,sys_guid
是自定义的函数名称,cn.bigdata.tools.HiveUDFOracleSysguid
是类名。
使用自定义函数的方式与内置函数相同:
sql
select sys_guid(32) from sp_t_re_valid_service limit 20;
起初,按照上述模板编写代码,运行时遇到了一个问题:生成的随机串逻辑只被执行了一次,所有行的随机串都是相同的,而不是每一行都有不同的随机串。
这个问题困扰了很多人,查阅了许多关于UDF开发的文章也没有找到解决方案。最终,灵感来源于Hive内置的rand()
函数,它能够为每行生成不同的随机数。因此,决定从rand()
函数的源码中寻找答案。
rand()
函数的核心逻辑位于org.apache.hadoop.hive.ql.udf
类中,大家可以自行查阅。
将核心逻辑复制到自己的UDF中测试后,发现问题依旧存在,核心逻辑只被执行了一次。关键在于@UDFType(deterministic = false)
注解,该注解用于指定UDF是否具有确定性,默认为true
。而rand()
函数的源码中,该属性被设置为false
。
经过测试,加入@UDFType(deterministic = false)
注解后,UDF能够正常工作,每行都能生成不同的32位字母数字随机串。