原文地址:http://wenku.baidu.com/link?url=fz-k6QR_cUyAIMybchXZ42hj4a6-q3CsT79Ub8OOxF5WFPvKJwGJpXiUKEkurrWK3imAlXDDpGu7fHRzwX2GFtiIKLlVHBl9qzHqGFMzma
在Hive中创建使用自定义函数
1 实际情况
2 网传的四种做法
2 适宜做法
3 原理(hive源代码)
4 实际情况
Hive提供了不少的函数,但是针对比较复杂的业务,还需要我们手动去定义一些函数来进行一些数据分析处理。Hive的自定义函数包括UDF、UDAF和UDTF,
定义模板请参考 http://hugh-wangp.iteye.com/blog/1472371。
网传的四种做法
1. 在HIVE会话中add 自定义函数的jar文件,然后创建function,继而使用函数;
2. 在进入HIVE会话之前先自动执行创建function,不用用户手工创建;
3. 把自定义的函数写到系统函数中,使之成为HIVE的一个默认函数,这样就不需要 create temporary function;
4. 在HIVE_HOME的bin目录下新建一个.hiverc的文件,把写好的注册语句写在里面。
1.在HIVE会话中add 自定义函数的jar文件,然后创建function,继而使用函数
hive> ADD JAR /home/hugh.wangp/UDFMd5.jar; Added /home/hugh.wangp/UDFMd5.jar to class path
hive> CREATE TEMPORARY FUNCTION udfmd5 AS 'udf.UDFMd5'; OK Time taken: 0.014 seconds
hive> select udfmd5('a') from dual; OK 0cc175b9c0f1b6a831c399e269772661
小结:这种方式的弊端是:每次打开新的会话,就要重新执行一遍如上的add jar和create temporary function的命令。
2.在进入HIVE会话之前先自动执行创建function
HIVE命令有个参数-i:在进入会话,待用户输入自己的HQL之前,先执行-i的参数。
我们只需要把add jar和create temporary function的命令写到一个文件中,并把这个文件传到-i的参数,如此一来省去了每次要手工创建的工作。
3.把自定义的函数写到系统函数中,使之成为HIVE的一个默认函数
(1)编写自己的UDF/UDAF/UDTF,并把代码放到 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/udf路径下
(2)修改 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java
以HIVE自带函数Trim()举例,自定义函数操作一样。
第一步: 写UDF代码UDFTrim.java并放到 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/udf/UDFTrim.java
第二步: 修改 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java文件
import org.apache.hadoop.hive.ql.udf.UDFTrim; registerUDF("trim", UDFTrim.class, false);
小结:这样的好处在于通过直接修改源代码可以永久性的使用该函数,甚至不需要像第2种方法运行hive –i的命令了;
但是缺点也正在于此,要添加一个函数就需要修改源代码。
4.在HIVE_HOME的bin目录下新建一个.hiverc的文件,把写好的注册语句写在里面
在.hiverc文件中加入以下内容:
ADD JAR /home/hugh.wangp/UDFMd5.jar;
CREATE TEMPORARY FUNCTION udfmd5 AS 'udf.UDFMd5';
小结:原理是,在运行./hive命令时,同时会加载HIVE_HOME/bin/.hiverc and $HOME/.hiverc作为初始化所需要的文件,
缺点是在不同机器的下运行时就要新建一个.hiverc文件,并且用jdbc连接时无效。
适宜做法 注:以下方法为针对IDH(transwarp也类似),且hive版本为0.9而言,
因为针对hive-0.13来说,有一种更为直接的方法:
CREATE FUNCTION myfunc AS 'myclass' USING JAR 'hdfs:///path/to/jar';
这会将jar包直接加到classpath中去。详见: https://cwiki.apache.org/confluence/display/Hive/HivePlugins
(1)下载集群上的/usr/lib/hive/lib/hive-builtins-0.9.0-Intel.jar,解压得到如下文件:
(2)编写自己的UDF/UDAF/UDTF,编译后将class文件(连同所在文件夹)放入到上面的hive-builtins-0.9.0-Intel文件夹下。
(3)修改class-info.xml
<ClassList>
<Class javaname="org.apache.hive.builtins.UDAFUnionMap" sqlname="union_map" />
</ClassList>
根据自定义函数的情况在ClassList标签中添加(Javaname为自定义函数路径,sqlname为自定义函数名称),如: <Class javaname="udaf.AddOneMonth" sqlname="nextM" />
(4)修改class-registration.sql CREATE TEMPORARY FUNCTION union_map AS 'org.apache.hive.builtins.UDAFUnionMap';
根据自定义函数的情况添加,保证函数名与第(3)步一致,如: CREATE TEMPORARY FUNCTION nextM AS 'udaf.AddOneMonth';
(5)重新将hive-builtins-0.9.0-Intel文件夹打jar包,并且上传到之前下载的目录覆盖旧jar包。
小结:这样做需要注意的是,你只能在你重新上传了新的jar包之后的那台机器上使用你自定义的hive函数,要是希望在集群的其他机器也可以使用,要拷贝该jar包到其他机器的相同目录下覆盖旧的jar包即可。
原理(hive源代码)
以下从hive的client连接开始,看hive的执行过程,仅考虑与上述“适宜做法”相关的部分代码——
Hive的client的入口(即执行hive命令后)为hive-cli-0.9.0-Intel.jar包中的org.apache.hadoop.hive.cli.CliDriver,
从main函数开始,初始化Hive环境变量,获取客户端提供的string或者file。
public static void main(String[] args) throws Exception {
int ret = run(args);
System.exit(ret);
}
public static int run(String[] args) throws Exception {
OptionsProcessor oproc = new OptionsProcessor();
if (!oproc.process_stage1(args)) {
return 1;
}
// NOTE: It is critical to do this here so that log4j is reinitialized
// before any of the other core hive classes are loaded
boolean logInitFailed = false;
String logInitDetailMessage;
try {
logInitDetailMessage = LogUtils.initHiveLog4j();
} catch (LogInitializationException e) {
logInitFailed = true;
logInitDetailMessage = e.getMessage();
}
CliSessionState ss = new CliSessionState(new HiveConf(SessionState.class));
...以下省略...
上面的CliSessionState继承自SessionState,所以初始化时执行了SessionState的构造方法:
public SessionState(HiveConf conf) {
this.conf = conf;
isSilent = conf.getBoolVar(HiveConf.ConfVars.HIVESESSIONSILENT);
ls = new LineageState();
overriddenConfigurations = new HashMap<String, String>();
overriddenConfigurations.putAll(HiveConf.getConfSystemProperties());
// Register the Hive builtins jar and all of its functions
try {
Class<?> pluginClass = Utilities.getBuiltinUtilsClass();
URL jarLocation = pluginClass.getProtectionDomain().getCodeSource().getLocation();//获取pluginclass所在jar包的路径
add_builtin_resource(ResourceType.JAR, jarLocation.toString());
FunctionRegistry.registerFunctionsFromPluginJar(jarLocation, pluginClass.getClassLoader());
} catch (Exception ex) {
throw new RuntimeException("Failed to load Hive builtin functions", ex);
}
}
至于上面粗线部分:
public static Class getBuiltinUtilsClass() throws ClassNotFoundException {
return Class.forName("org.apache.hive.builtins.BuiltinUtils");
}
而org.apache.hive.builtins.BuiltinUtils正在上面提到的 hive-builtins-0.9.0-Intel.jar当中。因此我们有了上面的“适宜做法”。
在Hive中创建使用自定义函数
1 实际情况
2 网传的四种做法
2 适宜做法
3 原理(hive源代码)
4 实际情况
Hive提供了不少的函数,但是针对比较复杂的业务,还需要我们手动去定义一些函数来进行一些数据分析处理。Hive的自定义函数包括UDF、UDAF和UDTF,
定义模板请参考 http://hugh-wangp.iteye.com/blog/1472371。
网传的四种做法
1. 在HIVE会话中add 自定义函数的jar文件,然后创建function,继而使用函数;
2. 在进入HIVE会话之前先自动执行创建function,不用用户手工创建;
3. 把自定义的函数写到系统函数中,使之成为HIVE的一个默认函数,这样就不需要 create temporary function;
4. 在HIVE_HOME的bin目录下新建一个.hiverc的文件,把写好的注册语句写在里面。
1.在HIVE会话中add 自定义函数的jar文件,然后创建function,继而使用函数
hive> ADD JAR /home/hugh.wangp/UDFMd5.jar; Added /home/hugh.wangp/UDFMd5.jar to class path
hive> CREATE TEMPORARY FUNCTION udfmd5 AS 'udf.UDFMd5'; OK Time taken: 0.014 seconds
hive> select udfmd5('a') from dual; OK 0cc175b9c0f1b6a831c399e269772661
小结:这种方式的弊端是:每次打开新的会话,就要重新执行一遍如上的add jar和create temporary function的命令。
2.在进入HIVE会话之前先自动执行创建function
HIVE命令有个参数-i:在进入会话,待用户输入自己的HQL之前,先执行-i的参数。
我们只需要把add jar和create temporary function的命令写到一个文件中,并把这个文件传到-i的参数,如此一来省去了每次要手工创建的工作。
3.把自定义的函数写到系统函数中,使之成为HIVE的一个默认函数
(1)编写自己的UDF/UDAF/UDTF,并把代码放到 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/udf路径下
(2)修改 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java
以HIVE自带函数Trim()举例,自定义函数操作一样。
第一步: 写UDF代码UDFTrim.java并放到 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/udf/UDFTrim.java
第二步: 修改 $HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java文件
import org.apache.hadoop.hive.ql.udf.UDFTrim; registerUDF("trim", UDFTrim.class, false);
小结:这样的好处在于通过直接修改源代码可以永久性的使用该函数,甚至不需要像第2种方法运行hive –i的命令了;
但是缺点也正在于此,要添加一个函数就需要修改源代码。
4.在HIVE_HOME的bin目录下新建一个.hiverc的文件,把写好的注册语句写在里面
在.hiverc文件中加入以下内容:
ADD JAR /home/hugh.wangp/UDFMd5.jar;
CREATE TEMPORARY FUNCTION udfmd5 AS 'udf.UDFMd5';
小结:原理是,在运行./hive命令时,同时会加载HIVE_HOME/bin/.hiverc and $HOME/.hiverc作为初始化所需要的文件,
缺点是在不同机器的下运行时就要新建一个.hiverc文件,并且用jdbc连接时无效。
适宜做法 注:以下方法为针对IDH(transwarp也类似),且hive版本为0.9而言,
因为针对hive-0.13来说,有一种更为直接的方法:
CREATE FUNCTION myfunc AS 'myclass' USING JAR 'hdfs:///path/to/jar';
这会将jar包直接加到classpath中去。详见: https://cwiki.apache.org/confluence/display/Hive/HivePlugins
(1)下载集群上的/usr/lib/hive/lib/hive-builtins-0.9.0-Intel.jar,解压得到如下文件:
(2)编写自己的UDF/UDAF/UDTF,编译后将class文件(连同所在文件夹)放入到上面的hive-builtins-0.9.0-Intel文件夹下。
(3)修改class-info.xml
<ClassList>
<Class javaname="org.apache.hive.builtins.UDAFUnionMap" sqlname="union_map" />
</ClassList>
根据自定义函数的情况在ClassList标签中添加(Javaname为自定义函数路径,sqlname为自定义函数名称),如: <Class javaname="udaf.AddOneMonth" sqlname="nextM" />
(4)修改class-registration.sql CREATE TEMPORARY FUNCTION union_map AS 'org.apache.hive.builtins.UDAFUnionMap';
根据自定义函数的情况添加,保证函数名与第(3)步一致,如: CREATE TEMPORARY FUNCTION nextM AS 'udaf.AddOneMonth';
(5)重新将hive-builtins-0.9.0-Intel文件夹打jar包,并且上传到之前下载的目录覆盖旧jar包。
小结:这样做需要注意的是,你只能在你重新上传了新的jar包之后的那台机器上使用你自定义的hive函数,要是希望在集群的其他机器也可以使用,要拷贝该jar包到其他机器的相同目录下覆盖旧的jar包即可。
原理(hive源代码)
以下从hive的client连接开始,看hive的执行过程,仅考虑与上述“适宜做法”相关的部分代码——
Hive的client的入口(即执行hive命令后)为hive-cli-0.9.0-Intel.jar包中的org.apache.hadoop.hive.cli.CliDriver,
从main函数开始,初始化Hive环境变量,获取客户端提供的string或者file。
public static void main(String[] args) throws Exception {
int ret = run(args);
System.exit(ret);
}
public static int run(String[] args) throws Exception {
OptionsProcessor oproc = new OptionsProcessor();
if (!oproc.process_stage1(args)) {
return 1;
}
// NOTE: It is critical to do this here so that log4j is reinitialized
// before any of the other core hive classes are loaded
boolean logInitFailed = false;
String logInitDetailMessage;
try {
logInitDetailMessage = LogUtils.initHiveLog4j();
} catch (LogInitializationException e) {
logInitFailed = true;
logInitDetailMessage = e.getMessage();
}
CliSessionState ss = new CliSessionState(new HiveConf(SessionState.class));
...以下省略...
上面的CliSessionState继承自SessionState,所以初始化时执行了SessionState的构造方法:
public SessionState(HiveConf conf) {
this.conf = conf;
isSilent = conf.getBoolVar(HiveConf.ConfVars.HIVESESSIONSILENT);
ls = new LineageState();
overriddenConfigurations = new HashMap<String, String>();
overriddenConfigurations.putAll(HiveConf.getConfSystemProperties());
// Register the Hive builtins jar and all of its functions
try {
Class<?> pluginClass = Utilities.getBuiltinUtilsClass();
URL jarLocation = pluginClass.getProtectionDomain().getCodeSource().getLocation();//获取pluginclass所在jar包的路径
add_builtin_resource(ResourceType.JAR, jarLocation.toString());
FunctionRegistry.registerFunctionsFromPluginJar(jarLocation, pluginClass.getClassLoader());
} catch (Exception ex) {
throw new RuntimeException("Failed to load Hive builtin functions", ex);
}
}
至于上面粗线部分:
public static Class getBuiltinUtilsClass() throws ClassNotFoundException {
return Class.forName("org.apache.hive.builtins.BuiltinUtils");
}
而org.apache.hive.builtins.BuiltinUtils正在上面提到的 hive-builtins-0.9.0-Intel.jar当中。因此我们有了上面的“适宜做法”。