引言曾经认为 Hive 自带的函数应该可以 cover 住我的日常所需, 心想那些需要使用自定义函数的场景是不是太奇葩, 谁知命运弄人, 自己还是碰上了.
需求很简单, 我需要模拟 Oracle 中的 SYS_GUID() 函数, 生成一个 32 位的字母数字随机串.
开发环境: Eclipse+Maven, 引入 Hive0.13.1 的依赖.
- package cn.fulong.bigdata.tools;
- 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; // 大写字母 65, 小写字母 96
- val.append((char) (choice + random.nextInt(26)));
- } else if ("num".equalsIgnoreCase(charOrNum)) // 数字
- {
- val.append(String.valueOf(random.nextInt(10)));
- }
- }
- return val.toString();
- }
- }
部署
步骤一:
打包该类, 上传到集群中, 进入 hive cli;
步骤二:
将 jar 包添加到 hive 的 classpath 中:
hive> add jar /home/hive-tools-sysguid-for-oracle.jar;
步骤三:
基于自定义 UDF 类创建自定义函数:
hive> create temporary function fbi_guid as 'cn.bigdata.tools.HiveUDFOracleSysguid';
其中, fbi_guid 是自己命名的函数名称, 将用于 hive sql 中, cn.bigdata.tools.HiveUDFOracleSysguid 是类名.
执行
和内置函数使用方法一样:
select fbi_guid(32) from sp_t_re_valid_service limit 20;
填坑记录
官网给出的示例代码是:
- package com.example.hive.udf;
- import org.apache.hadoop.hive.ql.exec.UDF;
- import org.apache.hadoop.io.Text;
- public final class Lower extends UDF {
- public Text evaluate(final Text s) {
- if (s == null) {
- return null;
- }
- return new Text(s.toString().toLowerCase());
- }
- }
但, 如果仅仅按照以上模板改写, 最终运行时会遇到一个很恶心的问题:
生成随机串的逻辑只被执行了一遍, 所有行的该字段字符串都是一样的, 并不会像预想的那样, 即每行一个随机串.
该问题请教了无数知名公司的大虾都没给出解决方案, 查阅无数篇 "UDF 开发详解" 类博客也没找到解药, 情急之下灵机一动, 想到了 HIVE 内置的 rand() 函数, 功能其实和我这个类似, 但是 rand() 就能很好的每行输出一个随机数, 所以决定从 rand() 的源码找答案.
rand() 函数的实现类是 org.apache.hadoop.hive.ql.udf., 各位可自行查阅.
我先将核心逻辑拷贝到自己的 UDF 中测试了一下, 发现问题还是存在, 核心逻辑只被执行了一遍, 每行的对应字段都是相同的数字.
出现该现象是好事, 因为问题的关键肯定在源码核心逻辑以外的某个角落!
通过观察核心逻辑以外可能对该结果造成影响的代码, 发现了 @UDFType(deterministic = false), 该注解指定了当前 UDF 的类型是否是确定性, 默认为 true, 然而, rand() 的源码中将该属性设置为了 false!
经测试,@UDFType(deterministic = false) 果然是结症所在, 加上这句注解后, 我的 UDF 可以正常执行了, 每行输出不同的 32 位随机字母数字串, over.
来源: http://www.bubuko.com/infodetail-3204293.html