hive udf&udaf开发

来源:互联网 发布:张孝祥java百度网盘 编辑:程序博客网 时间:2024/05/22 12:09

前言

由于之前对hadoop,hive源码了解不多,因此在进行udf和udaf开发时,遇到诸多问题,还是要有时间多看看源码。

UDF

进行UDF开发主要分为两种情况,根据UDF输入参数的类型进行区分。

调用UDF时传参是基本数据类型

若是hive调用UDF时传入的是基本数据类型如string,integer可以通过直接继承org.apache.hadoop.hive.ql.exec.UDF来进行开发。并且hadoop和java的数据类型之间不用进行转换处理(这在我们进行通用udf开发的时候要注意)。主要步骤:
1)继承org.apache.hadoop.hive.ql.exec.UDF
2)实现evaluate方法,此方法可进行重载
如下例子:

import org.apache.hadoop.hive.ql.exec.UDF;public class Add extends UDF {public Integer evaluate(Integer a, Integer b) {               if (null == a || null == b) {                               return null;               } return a + b;}public Double evaluate(Double a, Double b) {               if (a == null || b == null)                               return null;                               return a + b;               }}

调用UDF时传参是复杂类型

若是hive调用UDF时传入的是是复杂类型,如map,list等内嵌数据结构时,使用简单udf的开发方式是不行的,我们可以通过实现org.apache.hadoop.hive.ql.udf.generic.GenericUDF来写出通用的udf。
主要步骤:
1)实现org.apache.hadoop.hive.ql.udf.generic.GenericUDF
2) 重写initialize方法,此方法主要用来检测输入参数,并得到输入参数的Inspector,用来在evaluate方法解析参数(暂时这么理解)
3)重写evaluate方法,这是开发udf的主要逻辑部分
4)重写getDisplayString方法,此方法主要用来描述此udf。出错时会显示
以下是之前实现过对输入map类型进行处理的udf

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;import org.apache.hadoop.hive.ql.metadata.HiveException;import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;import org.apache.hadoop.hive.serde2.objectinspector.MapObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;import org.apache.hadoop.hive.serde2.objectinspector.primitive.IntObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;import org.apache.hadoop.io.Text;import java.util.HashMap;import java.util.Iterator;import java.util.Map;public class UDFmapValueScale extends GenericUDF{    private MapObjectInspector map;    private IntObjectInspector flag;    @Override    public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {        if(objectInspectors.length!=2){            throw new UDFArgumentLengthException("this only support 2 args");        }        ObjectInspector a=objectInspectors[0];        ObjectInspector b=objectInspectors[1];//        System.out.println(a.toString()+b.toString());        if(!(a instanceof MapObjectInspector) ||!(b instanceof IntObjectInspector)){            throw new UDFArgumentException("the argument must be a map");        }        this.map=(MapObjectInspector)a;        this.flag=(IntObjectInspector)b;        ObjectInspector returnType= ObjectInspectorFactory.getStandardMapObjectInspector(                PrimitiveObjectInspectorFactory.javaStringObjectInspector,                PrimitiveObjectInspectorFactory.javaStringObjectInspector        );        return returnType;    }    @Override    public Object evaluate(DeferredObject[] deferredObjects) throws HiveException {        Map<Text, Text> getMap = (Map<Text, Text>) this.map.getMap(deferredObjects[0].get());        if(getMap==null){            return null;        }        Integer i=(Integer) this.flag.getPrimitiveJavaObject(deferredObjects[1].get());        Map<String, String> retMap=new HashMap<>();        Iterator iter;        iter = getMap.entrySet().iterator();        while(iter.hasNext()){            Map.Entry entry = (Map.Entry) iter.next();            String key=entry.getKey().toString();            Double value=Double.parseDouble(entry.getValue().toString());            if(i!=0) {                value = (value + 1) * 100 / 2;            }            Double t=Math.rint(value);            retMap.put(key,t.toString());        }        return retMap;    }    @Override    public String getDisplayString(String[] strings) {        return "return a map" ;    }}

初次开发的时候,遇到过很多问题,记录两点
1)对于输入空值的处理,在hive中null是普遍存在的。起初我认为null值得判断应该在initialize中判断,但是null值没有类型。initialize中对输入参数的类型进行了判断,但是null值存在于任何数据类型中,在initialize中传入的是objectInspectors,并不是你要传入的值,因此不能通过判断是否为null进行实现。最后发现null处理需要在evaluate中 if(getMap==null){
return null;
}

2)数据类型,起初此处Map<Text, Text> getMap = (Map<Text, Text>) this.map.getMap(deferredObjects[0].get());我使用的是Map<String, String> getMap = (Map<String, String>) this.map.getMap(deferredObjects[0].get());,然后运行的时候报类型转换异常。也就是说我们在通过Inspectors.getmap()等方法获取输入参数时,返回的是hadoop的数据结构,我们在java中进行处理时,要通过tostring()或者get方法转为java数据类型。但是对于基本数据类型,我们直接可以通过getPrimitiveJavaObject方法返回java数据类型。

UDAF

UDAF主要是配合group by使用,到分组内数据进行聚合。UDAF的开发同UDF一样,根据输入参数类型不同进行区分。

传参基本数据类型

对于传参是基本类型的udaf开发我们可以简单的通过org.apache.hadoop.hive.ql.exec.UDAFEvaluator和org.apache.hadoop.hive.ql.exec.UDAF来进行实现。主要步骤:
1)继承org.apache.hadoop.hive.ql.exec.UDAF类
2)是在继承的UDAF类中实现org.apache.hadoop.hive.ql.exec.UDAFEvaluator接口
3)重写init方法,udaf的运行可分为map阶段可reduce阶段,init方法用于每个map的初始化,貌似在reduce时也会运行(暂且这么认为)
4)实现iterate方法,此方法用于输入数据的迭代,相当于map过程
5)实现terminatePartial方法,用于对iterate得到的数据进行处理,相当于combiner,Hive需要部分结果的时候会调用该方法,必须要返回一个封装了聚集计算当前状态的对象。
6)实现merge方法,用于结果聚合,接受的参数是terminatePartial的返回值
以下udaf实现了对一个字段每个值得出现次数进行计算,返回值为map,虽然通过普通hive函数也能实现,这里以此对udaf进行练习。

import org.apache.hadoop.hive.ql.exec.UDAFEvaluator;import org.apache.hadoop.hive.ql.exec.UDAF;import java.util.HashMap;import java.util.Iterator;import java.util.Map;public class UDAFMap extends UDAF{    public static class UDAFMapEvaluator implements UDAFEvaluator {        private Map<String,Integer> map;        public UDAFMapEvaluator(){            super();            init();        }        @Override        public void init() {            map=new HashMap<String, Integer>();        }        public boolean iterate(String s){            if(s ==null)                return true;            if(map.containsKey(s)){                map.put(s,map.get(s)+1);            }else{                map.put(s,1);            }            return true;        }        public Map<String,Integer> terminatePartial(){            if(map.isEmpty()){                return null;            }else{                return map;            }        }        public boolean merge(Map<String,Integer> m){            if(m!=null){                Iterator iter=m.entrySet().iterator();                while (iter.hasNext()){                    Map.Entry mapEntry=(Map.Entry)iter.next();                    String key=(String)mapEntry.getKey();                    Integer value=(Integer)mapEntry.getValue();                    if(map.containsKey(key)){                        map.put(key,map.get(key)+value);                    }else{                        map.put(key,value);                    }                }            }            return true;        }        public Map<String,Integer> terminate(){            return map;        }    }}

通用udaf

简单udaf,只能进行简单udaf的开发,传参只能是基本数据类型,并且貌似将要被启用,因此有必要学习一下通用udaf的开发。以下udaf的开发主要参考此文,写的比较详细,下面直接给上使用通用udaf实现以上简单udaf功能的代码。

import org.apache.hadoop.hive.ql.metadata.HiveException;import org.apache.hadoop.hive.ql.parse.SemanticException;import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;import org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver;import org.apache.hadoop.hive.serde2.objectinspector.MapObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;import org.apache.hadoop.hive.serde2.objectinspector.primitive.StringObjectInspector;import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;import org.apache.hadoop.io.IntWritable;import org.apache.hadoop.io.Text;import java.util.HashMap;import java.util.Map;public class GenericUDAFMap extends AbstractGenericUDAFResolver{    public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters) throws SemanticException{        return new GenericUDAFMapEvaluator();    }    public static class GenericUDAFMapEvaluator extends GenericUDAFEvaluator{        private StringObjectInspector inputOI;        private MapObjectInspector inputOI2;        public ObjectInspector init(Mode m,ObjectInspector[] parameters) throws HiveException{            assert (parameters.length==1);            super.init(m,parameters);            if(m==Mode.PARTIAL1||m==Mode.COMPLETE){                inputOI=(StringObjectInspector)parameters[0];            }else{                inputOI2=(MapObjectInspector)parameters[0];            }            return ObjectInspectorFactory.getStandardMapObjectInspector(PrimitiveObjectInspectorFactory.javaStringObjectInspector,                    PrimitiveObjectInspectorFactory.javaIntObjectInspector);        }        class MapBuffer implements AggregationBuffer{            Map<String,Integer> map;        }        @Override        public AggregationBuffer getNewAggregationBuffer() throws HiveException {            MapBuffer mb=new MapBuffer();            reset(mb);            return mb;        }        @Override        public void reset(AggregationBuffer aggregationBuffer) throws HiveException {            ((MapBuffer)aggregationBuffer).map=new HashMap<>();        }        @Override        public void iterate(AggregationBuffer aggregationBuffer, Object[] objects) throws HiveException {            //普通类型可以直接通过getPrimitiveJavaObject得到java类型            String s=this.inputOI.getPrimitiveJavaObject(objects[0]);            if(s!=null){                MapBuffer mb=(MapBuffer)aggregationBuffer;                if(mb.map.containsKey(s)){                    mb.map.put(s,mb.map.get(s)+1);                }else{                    mb.map.put(s,1);                }            }        }        @Override        public Object terminatePartial(AggregationBuffer aggregationBuffer) throws HiveException {            return ((MapBuffer)aggregationBuffer).map;        }        @Override        public void merge(AggregationBuffer aggregationBuffer, Object o) throws HiveException {            MapBuffer mb=(MapBuffer)aggregationBuffer;            //虽然上面定以了Map<String,Integer>,但是作为map的中间结果,猜测hadoop可能将其进行了序列化,变成了Text与IntWriteable            Map<Text,IntWritable> map=(Map<Text,IntWritable>)inputOI2.getMap(o);            for(Map.Entry<Text,IntWritable> entry: map.entrySet()){                String key=entry.getKey().toString();                Integer value=entry.getValue().get();                if(mb.map.containsKey(key)){                    mb.map.put(key,mb.map.get(key)+value);                }else{                    mb.map.put(key,value);                }            }        }        @Override        public Object terminate(AggregationBuffer aggregationBuffer) throws HiveException {            return ((MapBuffer)aggregationBuffer).map;        }    }}

问题记录:同样是数据类型转换问题,我们在进行通用udaf开发是一定要注意。

参考文献

http://blog.csdn.net/kent7306/article/details/50110067

原创粉丝点击