cindysz110 / blog

8 stars 1 forks source link

[Hadoop] Hive自定义函数 #23

Open cindygl opened 6 years ago

cindygl commented 6 years ago

当hive内置的函数无法满足需要时,可以自定义函数。hive允许我们在查询中轻易地使用自定义的函数。 UDF必须使用java语言,如果想要用其他语言来实现,可以考虑SELECT TRANSFORM查询,它允许hive与自定义的脚本通过stream进行交互,类似MapReduce Streaming和Pig Stream。

Hive中有三种类型的自定义函数,UDF(User-Defined Function)、UDAF(user-defined aggregation function)、UDTF(user-defined table-generation function)。三种函数的区别在于其输入和输出的行数:

UDF:一进一出,一行输入,一行输出 UDAF:多进一出,多行输入,一行输出 UDTF:一进多出,单行输入,多行输出(table)

用户自己开发UDF函数,UDF必须满足以下两个条件: 1)必须是UDF的子类 2)必须实现至少一个evaluate()方法 注意:evaluate不是UDF定义的方法(没有@Override),因为这个方法接受的参数类型和个数以及返回值类型都是自己定义的。hive使用反射机制来调用名为evaluate的方法。

要想在hive中完成自定义函数的操作,要按照如下流程进行操作: 1)用户自己创建maven的工程,编写java类并继承org.apache.hadoop.hive.ql.exec.UDF; 2)覆写evaluate函数,evaluate函数支持重载 3)把程序打成jar包放到hive所在服务器 4)进入hive客户端,添加jar包(将jar包放在hive指定目录或者hdfs上可以省略此步骤) 5)创建关联到java类的hive函数 6)hive命令中执行查询语句,得出自定义函数输出的结果

创建Hive自定义函数UDF - 临时函数

1. 在IDEA创建一个Maven项目

image

image

image

image

项目创建后会自动联网下载一些jar包,等待jar包下载完成。

2. 进入项目文件夹,删除src和test下的App和AppTest类

image

3. 修改pom.xml文件

3.1 修改打包类型,默认是war包,修改成jar包

  <groupId>hive-group</groupId>
  <artifactId>hivesample</artifactId>
  <packaging>jar</packaging>      # 添加一行
  <version>1.0-SNAPSHOT</version>

3.2 定义hive和hadoop的版本号,方便其他地方引用

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>     
    <hive.version>1.1.0-cdh5.7.0</hive.version>     # 添加一行
    <hadoop.version>2.6.0-cdh5.7.0</hadoop.version>      # 添加一行
  </properties>

3.3 因为用的是cdh版本的hadoop和hive,所以需要添加cdh的资源库

# 添加这一段
  <repositories>
    <repository>
      <id>cloudera</id>
      <url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
    </repository>
  </repositories>

3.4 添加hadoop和hive的依赖

# 在  <dependencies>标签下添加
  <dependencies>
    <dependency>
      <groupId>org.apache.hadoop</groupId>
      <artifactId>hadoop-common</artifactId>
      <version>${hadoop.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.hive</groupId>
      <artifactId>hive-exec</artifactId>
      <version>${hive.version}</version>
    </dependency>

4. 新建一个java类,并且打成jar包

4.1 新建一个类,取名HelloUDF。 功能是:输入一个字串xxx,返回Hello xxx。

image

在新建的java类HelloUDF.java中写入

package com.bigdata.bigdata;

import org.apache.hadoop.hive.ql.exec.UDF;

public class HelloUDF extends UDF {

    public String evaluate(String input) {
        return "Hello:" + input;
    }

    public String evaluate(String input, String input2) {
        return "Hello:" + input + ":" + input2;
    }

    public static void main(String[] args) {
        HelloUDF udf = new HelloUDF();
        System.out.println(udf.evaluate("aaa","bbb"));
    }
}

4.2 运行一下 image

4.3 打成jar包 image

4.4 打包成功 image

5. hive创建临时函数

5.1 创建临时函数

创建临时函数有两种方法:   第一种是将jar包上传到指定目录下,hive CLI里面add jar导入   第二种是将jar包直接上传到hive家目录下的指定目录下,这种方式不需要导入jar包

方法一:上传jar包到/home/hadoop/lib目录下

[hadoop@hadoop01 lib]$ pwd
/home/hadoop/lib
[hadoop@hadoop01 lib]$ ls -ltr
total 4
-rw-r--r--. 1 hadoop hadoop 3086 Jun 28 21:12 hive-1.0-SNAPSHOT.jar
[hadoop@hadoop01 lib]$ 

方法二:上传jar包到hive家目录的指定目录下

[hadoop@hadoop01 ~]$ cd $HIVE_HOME
[hadoop@hadoop01 apache-hive-1.1.0-cdh5.7.0-bin]$ mkdir auxlib
[hadoop@hadoop01 apache-hive-1.1.0-cdh5.7.0-bin]$ cd auxlib/
[hadoop@hadoop01 auxlib]$ cp /tmp/hive-1.0-SNAPSHOT.jar .
[hadoop@hadoop01 auxlib]$ ls -ltr
total 4
-rw-r--r--. 1 hadoop hadoop 3086 Jun 28 22:04 hive-1.0-SNAPSHOT.jar
[hadoop@hadoop01 auxlib]$ 

5.2 创建hive临时函数并测试

# 如果使用上面的方法一,需要在hive中导入jar包。如果使用的是方法二,不需要导入jar包
[hadoop@hadoop01 ~]$ hive
hive> use hive;
OK
Time taken: 0.567 seconds
hive> add jar /home/hadoop/lib/hive-1.0-SNAPSHOT.jar;     # 当jar包不在hive家目录下的指定目录里面时需要add jar
Added [/home/hadoop/lib/hive-1.0-SNAPSHOT.jar] to class path
Added resources: [/home/hadoop/lib/hive-1.0-SNAPSHOT.jar]
hive> list jars;             # 可以看到刚刚添加的jar包
/home/hadoop/lib/hive-1.0-SNAPSHOT.jar
hive>

# 创建临时函数
hive> create temporary function sayHello as 'com.bigdata.bigdata.HelloUDF';
OK
Time taken: 0.023 seconds
hive> 
# 查看sayhello是否已经在函数列表
hive> show functions;
...
sayhello      # 可以看到HelloUDF被转成了小写,已经在function列表里面了
...
~
Time taken: 0.21 seconds, Fetched: 211 row(s)
hive> 
# 借用之前创建的dual表来测试
hive> select sayhello('cindy') from dual;
OK
Hello:cindy     # sayhello函数已经存在并可以正常使用
Time taken: 0.476 seconds, Fetched: 1 row(s)
hive> 

5.3 临时函数删除

hive> drop temporary function sayhello;
OK
Time taken: 0.004 seconds
hive> 

注意:这种方式导入的是临时函数,是单session的,只对当前窗口有效。退出hive CLI重新进入,或者打开其他窗口时候sayhello函数不会存在。

创建Hive自定义函数UDF - 永久函数

上面创建的函数是临时的,UDF函数本身不会存在hive里面,每次调用之前需要创建临时函数再使用。要想UDF永久保存在hive中随时都可以直接使用,可以修改hive源代码,重新编译,注册函数来实现。但是这种做法风险很大。

1. 将jar包放到HDFS上

[hadoop@hadoop01 ~]$ hadoop fs -ls /
Found 2 items
drwx-wx-wx   - hadoop supergroup          0 2018-06-26 21:27 /tmp
drwxr-xr-x   - hadoop supergroup          0 2018-06-26 23:35 /user
[hadoop@hadoop01 ~]$ hadoop fs -mkdir /lib
[hadoop@hadoop01 ~]$ hadoop fs -put /tmp/hive-1.0-SNAPSHOT.jar /lib/
[hadoop@hadoop01 ~]$ hadoop fs -ls /lib/
Found 1 items
-rw-r--r--   1 hadoop supergroup       3086 2018-06-28 22:19 /lib/hive-1.0-SNAPSHOT.jar
[hadoop@hadoop01 ~]$ 

2. 创建函数

hive> 
    > CREATE FUNCTION aaaaa AS 'com.bigdata.bigdata.HelloUDF'
    > USING JAR 'hdfs://10.132.37.38:9000/lib/hive-1.0-SNAPSHOT.jar';
converting to local hdfs://10.132.37.38:9000/lib/hive-1.0-SNAPSHOT.jar
Added [/tmp/6c0e6633-74b8-4640-9f55-0713b9463dce_resources/hive-1.0-SNAPSHOT.jar] to class path
Added resources: [hdfs://10.132.37.38:9000/lib/hive-1.0-SNAPSHOT.jar]
OK
Time taken: 0.046 seconds
hive>
# 使用dual表进行测试
hive> select aaaaa('cindy') from dual;
OK
Hello:cindy
Time taken: 0.345 seconds, Fetched: 1 row(s)
hive>

这种方式创建的函数会永久存在hive中。