UDF、UDAF和UDTF开发模板

0.背景

Hive是一种构建在Hadoop上的数据仓库,Hive把SQL查询转换为一系列在Hadoop集群中运行的MapReduce作业,是MapReduce更高层次的抽象,不用编写具体的MapReduce方法。Hive将数据组织为表,这就使得HDFS上的数据有了结构,元数据即表的模式,都存储在名为metastore的数据库中。

1.UDF是什么?

hive的类SQL预发给数据挖掘工作者带来了很多便利,海量数据通过简单的sql就可以完成分析,有时候hive提供的函数功能满足不了业务需要,就需要我们自己来写UDF函数来辅助完成,下面用一个简单的例子说明过程,以及注意事项。

UDF函数其实就是一个简单的函数,执行过程就是在Hive转换成mapreduce程序后,执行java方法,类似于像Mapreduce执行过程中加入一个插件,方便扩展. UDF只能实现一进一出的操作,如果需要实现多进一出,则需要实现UDAF .

Hive可以允许用户编写自己定义的函数UDF,来在查询中使用。

2.UDF类型


Hive中有3种UDF:
UDF:操作单个数据行,产生单个数据行;
UDAF:操作多个数据行,产生一个数据行。
UDTF:操作一个数据行,产生多个数据行一个表作为输出。

3.如何构建UDF

用户构建的UDF使用过程如下:
第一步:继承UDF或者UDAF或者UDTF,实现特定的方法。
第二步:将写好的类打包为jar。如hivefirst.jar.
第三步:进入到Hive外壳环境中,利用add jar /home/hadoop/hivefirst.jar.注册该jar文件
第四步:为该类起一个别名,create temporary function mylength as 'com.whut.StringLength';这里注意UDF只是为这个Hive会话临时定义的。
第五步:在select中使用mylength();

4.UDF_Demo

package whut;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
//UDF是作用于单个数据行,产生一个数据行
//用户必须要继承UDF,且必须至少实现一个evalute方法,该方法并不在UDF中
//但是Hive会检查用户的UDF是否拥有一个evalute方法
public class Strip extends UDF{
    private Text result=new Text();
    //自定义方法
    public Text evaluate(Text str)
    {
      if(str==null)
        return null;
        result.set(StringUtils.strip(str.toString()));
        return result;
    }
    public Text evaluate(Text str,String stripChars)
    {
        if(str==null)
            return null;
        result.set(StringUtils.strip(str.toString(),stripChars));
        return result;
    }
}

 

注意事项:
   1,一个用户UDF必须继承org.apache.hadoop.hive.ql.exec.UDF;
   2,一个UDF必须要包含有evaluate()方法,但是该方法并不存在于UDF中。evaluate的参数个数以及类型都是用户自己定义  的。在使用的时候,Hive会调用UDF的evaluate()方法。

5.UDTF_Demo

public class NameParserGenericUDTF extends GenericUDTF {
 
	  private PrimitiveObjectInspector stringOI = null;
 
	  @Override
	  public StructObjectInspector initialize(ObjectInspector[] args) UDFArgumentException {
 
	    if (args.length != 1) {
	      throw new UDFArgumentException("NameParserGenericUDTF() takes exactly one argument");
	    }
 
	    if (args[0].getCategory() != ObjectInspector.Category.PRIMITIVE
	        && ((PrimitiveObjectInspector) args[0]).getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.STRING) {
	      throw new UDFArgumentException("NameParserGenericUDTF() takes a string as a parameter");
	    }
        
	    // 输入格式(inspectors)
	    stringOI = (PrimitiveObjectInspector) args[0];
 
	    // 输出格式(inspectors) -- 有两个属性的对象
	    List<String> fieldNames = new ArrayList<String>(2);
	    List<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(2);
	    fieldNames.add("name");
	    fieldNames.add("surname");
	    fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
	    fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
	    return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
	  }
	  	  
	  public ArrayList<Object[]> processInputRecord(String name){
		    ArrayList<Object[]> result = new ArrayList<Object[]>();
		  
		    // 忽略null值与空值
		    if (name == null || name.isEmpty()) {
		      return result;
		    }
		    
		    String[] tokens = name.split("\\s+");
		    
		    if (tokens.length == 2){
		    	result.add(new Object[] { tokens[0], tokens[1] });
		    }else if (tokens.length == 4 && tokens[1].equals("and")){
		    	result.add(new Object[] { tokens[0], tokens[3] });
		    	result.add(new Object[] { tokens[2], tokens[3] });
		    }
		    
		    return result;
	  }
	  
	  @Override
	  public void process(Object[] record) throws HiveException {
 
	    final String name = stringOI.getPrimitiveJavaObject(record[0]).toString();
 
	    ArrayList<Object[]> results = processInputRecord(name);
 
	    Iterator<Object[]> it = results.iterator();
	    
	    while (it.hasNext()){
	    	Object[] r = it.next();
	    	forward(r);
	    }
	  }
 
	  @Override
	  public void close() throws HiveException {
	    // do nothing
	  }
}

分开的UDTF参考:https://www.jianshu.com/p/ac352ceab9cd 

6.UDAF_Demo

package whut;
import org.apache.hadoop.hive.ql.exec.UDAF;
import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;
import org.apache.hadoop.io.IntWritable;
//UDAF是输入多个数据行,产生一个数据行
//用户自定义的UDAF必须是继承了UDAF,且内部包含多个实现了exec的静态类
public class MaxiNumber extends UDAF{
    public static class MaxiNumberIntUDAFEvaluator implements UDAFEvaluator{
        //最终结果
        private IntWritable result;
        //负责初始化计算函数并设置它的内部状态,result是存放最终结果的
        @Override
        public void init() {
            result=null;
        }
        //每次对一个新值进行聚集计算都会调用iterate方法
        public boolean iterate(IntWritable value)
        {
            if(value==null)
                return false;
            if(result==null)
              result=new IntWritable(value.get());
            else
              result.set(Math.max(result.get(), value.get()));
            return true;
        }
                                                                                                                                  
        //Hive需要部分聚集结果的时候会调用该方法
        //会返回一个封装了聚集计算当前状态的对象
        public IntWritable terminatePartial()
        {
            return result;
        }
        //合并两个部分聚集值会调用这个方法
        public boolean merge(IntWritable other)
        {
            return iterate(other);
        }
        //Hive需要最终聚集结果时候会调用该方法
        public IntWritable terminate()
        {
            return result;
        }
    }
}

注意事项:
1.用户的UDAF必须继承了org.apache.hadoop.hive.ql.exec.UDAF;
2.用户的UDAF必须包含至少一个实现了org.apache.hadoop.hive.ql.exec的静态类,诸如常见的实现了 UDAFEvaluator。
3.一个计算函数必须实现的5个方法的具体含义如下:

  • init():主要是负责初始化计算函数并且重设其内部状态,一般就是重设其内部字段。一般在静态类中定义一个内部字段来存放最终的结果。

  • iterate():每一次对一个新值进行聚集计算时候都会调用该方法,计算函数会根据聚集计算结果更新内部状态。当输入值合法或者正确计算了,则就返回true。

  • terminatePartial():Hive需要部分聚集结果的时候会调用该方法,必须要返回一个封装了聚集计算当前状态的对象。

  • merge():Hive进行合并一个部分聚集和另一个部分聚集的时候会调用该方法。

  • terminate():Hive最终聚集结果的时候就会调用该方法。计算函数需要把状态作为一个值返回给用户。

4.部分聚集结果的数据类型和最终结果的数据类型可以不同。

参考:

https://blog.csdn.net/WYpersist/article/details/80314352 写的很好 很详细

https://blog.csdn.net/kent7306/article/details/50200061

https://www.jianshu.com/p/ac352ceab9cd

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值