15 Hive 函数与自定义函数

来源:互联网 发布:软件开发代码 编辑:程序博客网 时间:2024/05/18 22:54

Hive函数介绍

HQL内嵌函数只有195个函数(包括操作符,使用命令show functions查看),基本能够胜任基本的hive开发,但是当有较为复杂的需求的时候,可能需要进行定制的HQL函数开发。HQL支持三种方式来进行功能的扩展(只支持使用java编写实现自定义函数),分别是:UDF(User-Defined Function)、UDAF(User-Defined Aggregate Function)和UDTF(User-Defined Table-Generating Function)。当我们使用java语言进行开发完成后,将生成的jar包移到linux机器(hive机器)上,进行函数的创建,然后进行使用即可。

hive (default)> show functions;show functions "x.*";

函数创建命令

HQL函数的创建一般分为以下几步:
1. 添加jar(0.13.*不支持hdfs上的jar添加,14版本才开始支持)
add jar linux_jar_path
2. 创建function,语法规则如下:
create [temporary] function [dbname.]function_name AS class_name;
class_name:是package+类名的全称。
3. 使用function,和使用其他函数一样。

函数删除命令

我们可以通过drop命令删除自定义函数,语法规则如下:
drop [temporary] function [if exists] [dbname.]function_name;

创建Function命令: CREATE FUNCTION [db_name.]function_name AS class_name [USING JAR|FILE|ARCHIVE ‘file_uri’ [, JAR|FILE|ARCHIVE ‘file_uri’] ]; file_uri可以为hdfs上文件路径
删除: drop function function_name;
显示: show functions [‘regex’]

一、自定义UDF介绍

UDF(User-Defined Function)支持一个输入产生一个输出,是一个最常用的自定义函数类型。实现自定义UDF要求继承类org.apache.hadoop.hive.ql.exec.UDF,并且在自定义UDF类中重载实现evaluate方法,我们可以通过重载多个evaluate方法达到函数参数多样化的需求。
实现案例:实现一个大小写转换的函数,要求函数通过参数的不同决定是进行那种转换,默认是转换为小写。

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">     <modelVersion>4.0.0</modelVersion>     <groupId>beifeng14</groupId>     <artifactId>beifeng14</artifactId>     <version>0.0.1</version>     <dependencies>           <dependency>                <groupId>org.apache.hadoop</groupId>                <artifactId>hadoop-common</artifactId>                <version>2.5.0</version>           </dependency>           <dependency>                <groupId>org.apache.hadoop</groupId>                <artifactId>hadoop-hdfs</artifactId>                <version>2.5.0</version>           </dependency>           <dependency>                <groupId>org.apache.hadoop</groupId>                <artifactId>hadoop-client</artifactId>                <version>2.5.0</version>           </dependency>           <dependency>                <groupId>org.apache.hive</groupId>                <artifactId>hive-exec</artifactId>                <version>0.13.1</version>           </dependency>           <dependency>                <groupId>jdk.tools</groupId>                <artifactId>jdk.tools</artifactId>                <version>1.7</version>                <scope>system</scope>                <systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>           </dependency>     </dependencies>     <build>           <sourceDirectory>src</sourceDirectory>           <plugins>                <plugin>                     <artifactId>maven-compiler-plugin</artifactId>                     <version>3.5.1</version>                     <configuration>                           <source>1.7</source>                           <target>1.7</target>                     </configuration>                </plugin>           </plugins>     </build></project>

UDFLowerOrUpperCase

package com.beifeng.hive.ql;import org.apache.hadoop.hive.ql.exec.UDF;/** * 自定义UDF,要求继承UDF,并重载实现evaluate方法 * @author Administrator * */import org.apache.hadoop.io.Text;public class UDFLowerOrUpperCase extends UDF {     /**      * 转换成小写      *      * @param t      * @return      */     public Text evaluate(Text t) {           // 默认进行小写转换           return this.evaluate(t, "lower");     }     /**      * 对参数t进行大小写转换      *      * @param t      * @param lowerOrUpper      *            如果该值为lower,则进行小写转换,如果该值为upper则进行大写转换,其它情况不进行转换。      * @return      */     public Text evaluate(Text t, String lowerOrUpper) {           if (t == null) {                return t;           }           if ("lower".equals(lowerOrUpper)) {                return new Text(t.toString().toLowerCase());           } else if ("upper".equals(lowerOrUpper)) {                return new Text(t.toString().toUpperCase());           }           // 转换参数错误的情况下,直接返回原本的值           return t;     }}

导出:右击项目–runas–maven install

使用database:beifeng13
1. 实现一个大小写转换的自定义函数。
实现完成后
1. 添加jar:add jar /home/hadoop/bigdater/jobs/beifeng14-0.0.1.jar;
2. 创建function:
create temporary function temp_f as ‘com.beifeng.hive.ql.UDFLowerOrUpperCase’;
永久function:
create function lower_upper as ‘com.beifeng.hive.ql.UDFLowerOrUpperCase’;
3. 使用
select [dbname.]lower_upper(studentname) from beifeng13.students;
【function与数据库关联,在其它的数据库则使用dbname.function来使用】

二、UDAF介绍

UDAF(User-Defined Aggregate Function)支持多个输入,一个输出。在原来的版本中可以通过继承UDAF类来实现自定义UDAF,但是现在hive已经将这个类标注为弃用状态。现在一般通过继承AbstractGenericUDAFResolver类来实现自定义UDAF,通过这种方式要求实现自定义的GenericUDAFEvaluator类。也就是说在现在的hive版本中,实现自定义UDAF,那么需要实现两个类,分别是AbstractGenericUDAFResolver和GenericUDAFEvaluator。

AbstractGenericUDAFResolver介绍

AbstractGenericUDAFResolver类主要作用就是根据hql调用时候的函数参数来获取具体的GenericUDAFEvaluator实例对象,也就是说实现方法getEvaluator即可,该方法的主要作用就是根据参数的不同返回不同的evaluator实例对象,实现多态性。

GenericUDAFEvaluator介绍

GenericUDAFEvaluator类主要作用就是根据job的不同阶段执行不同的方法。hive通过GenericUDAFEvaluator.Model来确定job的执行阶段。
PARTIAL1:从原始数据到部分聚合,会调用方法iterate和terminatePartial方法;
PARTIAL2:从部分数据聚合和部分数据聚合,会调用方法merge和terminatePartial;
FINAL:从部分数据聚合到全部数据聚合,会调用方法merge和terminate;
COMPLETE:从原始数据到全部数据聚合,会调用方法iterate和terminate。
除了上面提到的iterate、merge、terminate和terminatePartial以外,还有init(初始化并返回返回值的类型)、getNewAggregationBuffer(获取新的buffer对象,也就是方法之间传递参数的对象),reset(重置buffer对象)。

自定义UDAF(多个输入,一个输出) 和group by一起操作
1. UDAF介绍
PARTIAL1:iterate&terminatePartial map的输入阶段
PARTIAL2: merge&terminatePartial map输出阶段
FINAL: merge&terminate reducer输入&reducer输出阶段
COMPLETE: iterate&terminate 没有reducer的map输出阶段
2. UDAF实例:实现自定义sum函数
create function self_sum as ‘com.beifeng.hive.ql.UDAFSumCase’;

UDAF案例:

实现一个自定义的sum函数。要求函数支持整形和浮点型的sum操作。

package com.beifeng.hive.ql;import org.apache.hadoop.hive.ql.exec.UDFArgumentException;import org.apache.hadoop.hive.ql.metadata.HiveException;import org.apache.hadoop.hive.ql.parse.SemanticException;import org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver;import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFParameterInfo;import org.apache.hadoop.hive.serde2.io.DoubleWritable;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.primitive.AbstractPrimitiveWritableObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorUtils;import org.apache.hadoop.io.LongWritable;/** * 自定义UDAF函数实现 * * @author gerry * */@SuppressWarnings("deprecation")public class UDAFSumCase extends AbstractGenericUDAFResolver {     @Override     public GenericUDAFEvaluator getEvaluator(GenericUDAFParameterInfo info) throws SemanticException {           if (info.isAllColumns()) {                // 函数允许使用“*”查询的时候会返回true。                throw new SemanticException("不支持使用*查询");           }           // 获取函数参数列表           ObjectInspector[] inspectors = info.getParameterObjectInspectors();           if (inspectors.length != 1) {                throw new UDFArgumentException("只支持一个参数进行查询");           }           AbstractPrimitiveWritableObjectInspector apwoi = (AbstractPrimitiveWritableObjectInspector) inspectors[0];           switch (apwoi.getPrimitiveCategory()) {           case BYTE:           case INT:           case SHORT:           case LONG:                // 都是进行整修的sum操作                return new SumLongEvaluator();           case FLOAT:           case DOUBLE:                // 进行浮点型的sum操作                return new SumDoubleEvaluator();           default:                throw new UDFArgumentException("参数异常");           }     }     /**      * 进行浮点型操作      *      * @author gerry      *      */     static class SumDoubleEvaluator extends GenericUDAFEvaluator {           private PrimitiveObjectInspector inputOI;           static class SumDoubleAgg implements AggregationBuffer {                double sum;                boolean empty;           }           @Override           public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {                super.init(m, parameters);                this.inputOI = (PrimitiveObjectInspector) parameters[0];                return PrimitiveObjectInspectorFactory.writableDoubleObjectInspector;           }           @Override           public AggregationBuffer getNewAggregationBuffer() throws HiveException {                SumDoubleAgg sda = new SumDoubleAgg();                this.reset(sda);                return sda;           }           @Override           public void reset(AggregationBuffer agg) throws HiveException {                SumDoubleAgg sda = (SumDoubleAgg) agg;                sda.empty = true;                sda.sum = 0;           }           @Override           public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {                this.merge(agg, parameters[0]);           }           @Override           public Object terminatePartial(AggregationBuffer agg) throws HiveException {                return this.terminate(agg);           }           @Override           public void merge(AggregationBuffer agg, Object partial) throws HiveException {                if (partial != null) {                     SumDoubleAgg sda = (SumDoubleAgg) agg;                     sda.sum += PrimitiveObjectInspectorUtils.getDouble(partial, inputOI);                     sda.empty = false;                }           }           @Override           public Object terminate(AggregationBuffer agg) throws HiveException {                SumDoubleAgg sda = (SumDoubleAgg) agg;                if (sda.empty) {                     return null;                }                return new DoubleWritable(sda.sum);           }     }     /**      * 进行整形的sum操作      *      * @author gerry      *      */     static class SumLongEvaluator extends GenericUDAFEvaluator {           private PrimitiveObjectInspector inputOI;           @Override           public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException {                super.init(m, parameters);                if (parameters.length != 1) {                     throw new UDFArgumentException("参数异常");                }                inputOI = (PrimitiveObjectInspector) parameters[0];                return PrimitiveObjectInspectorFactory.writableLongObjectInspector;           }           /**            * 自定义类型            *            * @author gerry            *            */           static class SumLongAgg implements AggregationBuffer {                long sum;                boolean empty;           }           @Override           public AggregationBuffer getNewAggregationBuffer() throws HiveException {                SumLongAgg sla = new SumLongAgg();                this.reset(sla);                return sla;           }           @Override           public void reset(AggregationBuffer agg) throws HiveException {                SumLongAgg sla = (SumLongAgg) agg;                sla.sum = 0;                sla.empty = true;           }           /**            * 循环处理会调用的方法            */           @Override           public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException {                if (parameters.length != 1) {                     throw new UDFArgumentException("参数异常");                }                this.merge(agg, parameters[0]);           }           /**            * 部分聚合后的数据输出            *            */           @Override           public Object terminatePartial(AggregationBuffer agg) throws HiveException {                return this.terminate(agg);           }           /**            * 合并操作            */           @Override           public void merge(AggregationBuffer agg, Object partial) throws HiveException {                if (partial != null) {                     SumLongAgg sla = (SumLongAgg) agg;                     sla.sum += PrimitiveObjectInspectorUtils.getLong(partial, inputOI);                     sla.empty = false;                }           }           /**            * 全部输出            */           @Override           public Object terminate(AggregationBuffer agg) throws HiveException {                SumLongAgg sla = (SumLongAgg) agg;                if (sla.empty) {                     return null;                }                return new LongWritable(sla.sum);           }     }}

三、UDTF介绍–主要用于收集日志的解析

UDTF(User-Defined Table-Generating Function)支持一个输入多个输出。一般用于解析工作,比如说解析url,然后获取url中的信息。要求继承类org.apache.hadoop.hive.ql.udf.generic.GenericUDTF,实现方法:initialize(返回返回值的参数类型)、process具体的处理方法,一般在这个方法中会调用父类的forward方法进行数据的写出、close关闭资源方法,最终会调用close方法,同MR程序中的cleanUp方法。

UDTF实例:解析爬虫数据,从数据中读取产品id、产品名称、价格。
1. 实现udtf,并打包jar,copy到linux机器上。

package com.beifeng.hive.ql;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.StringTokenizer;import org.apache.hadoop.hive.ql.exec.UDFArgumentException;import org.apache.hadoop.hive.ql.metadata.HiveException;import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;public class UDTFCase extends GenericUDTF {     @Override     public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {           // 比较参数大小           if (argOIs.getAllStructFieldRefs().size() != 1) {                throw new UDFArgumentException("参数异常");           }           ArrayList<String> fieldNames = new ArrayList<>();           ArrayList<ObjectInspector> fieldOIs = new ArrayList<>();           fieldNames.add("id");           fieldNames.add("name");           fieldNames.add("price");          fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);          fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);          fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);           return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);     }     // "p_id":"100001","p_name":"<张家界-凤凰-天门山-玻璃栈道飞机5日游>","price":"2141"     // pid:xxx     // pname:xxx     // price:xxx     @Override     public void process(Object[] args) throws HiveException {           if (args == null && args.length != 1) {                return;           }           // 只有一个参数的情况           String line = args[0].toString();           // 调用函数解析字符串           Map<String, String> map = transfoerContent2Map(line);           List<String> result = new ArrayList<>();           result.add(map.get("p_id"));           result.add(map.get("p_name"));           result.add(map.get("price"));           super.forward(result.toArray(new String[0]));     }     @Override     public void close() throws HiveException {           // nothing           super.forward(new String[] { "12345689", "close", "123" });     }     /**      * 转换字符串为map对象      *      * @param content      * @return      */     static Map<String, String> transfoerContent2Map(String content) {           Map<String, String> map = new HashMap<String, String>();           int i = 0;           String key = "";           // 删掉不需要的字符           StringTokenizer tokenizer = new StringTokenizer(content, "({|}|\"|:|,)");           while (tokenizer.hasMoreTokens()) {                if (++i % 2 == 0) {                     // 当前的值是value                     map.put(key, tokenizer.nextToken());                } else {                     // 当前的值是key                     key = tokenizer.nextToken();                }           }           return map;     }}
  1. 创建hbase关联表(将hbase中数据导入hive表中。。)
    create external table hive_data(rowkey string,content string) row format serde ‘org.apache.hadoop.hive.hbase.HBaseSerDe’ stored by ‘org.apache.hadoop.hive.hbase.HBaseStorageHandler’ with serdeproperties(‘hbase.columns.mapping’=’:key,f:content’) tblproperties (‘hbase.table.name’=’data’);
  2. 创建函数
    add jar /home/hadoop/bigdater/jobs/beifeng14-0.0.1.jar;
    create function f1 as ‘com.beifeng.hive.ql.UDTFCase’;
    4.运行f1函数
    select f1(content) as(id,name,price) from hive_data;

四、常用的三种集成自定义函数的方式

首先要求创建的function是永久function,不能是临时function。
第一种:修改hive-site.xml文件,添加参数hive.aux.jars.path,value为jar包的linux本地路径,要求是以file:///开头的绝对路径。
第二种:直接将jar包移动到hive的lib文件夹中。
第三种:将jar包移动到hdfs上,然后在创建function的时候指定function使用的hdfs上的jar文件绝对路径(包括hdfs://hh:8020/前缀),这样在使用的时候,hive会自动将jar下载到本地进行缓存的。

  1. 修改hive-site.xml
<property>    <name>hive.aux.jars.path</name>    <value>file:///home/hadoop/bigdater/jobs/beifeng14-0.0.1.jar</value></property>

最常用的:使用将jar上传到hdfs中,然后再进行操作的方式。
1. 上传文件到hdfs:【hive上传特别快】
hive> dfs -put /home/hadoop/bigdater/jobs/beifeng14-0.0.1.jar /beifeng/beifeng14-0.0.1.jar
2. 创建函数:create function f2 as ‘com.beifeng.hive.ql.UDTFCase’ using jar ‘hdfs://hh:8020/beifeng/beifeng14-0.0.1.jar’;
3. 正常使用

另外一种hive集成自定义函数的方式【很少用】
我们可以通过修改hive的源码,进行自定义函数的添加,添加完成后,我们就不需要再手动创建函数。添加步骤如下:
1. 假设自定义函数的整个包名为com.beifeng.ql.udf.UDFTest, jar文件为beifengUserUDF.jar。将该jar包移动到hive的lib文件夹中。
2. 修改hive源文件
$HIVE_HOME/src/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java,添加import com.beifeng.ql.udf.UDFTest; registerUDF(“test”, UDFTest.class,false);
3. 编译hive后,进行jar包的替换,然后就可以使用函数了。

1 0