挖挖Hive的代码(一)——UDF

系列第一篇,先扯扯。Hive的东西太多了,想一篇文章介绍完也是可以的,但是没有意义。所以我会分几篇写下我的“挖掘”经历,当然了,我也没打算把Hive所有的内容都挖一遍,只是记录下我感兴趣的、没见别人挖过的……

         Hive对查询语句的解析过程,在淘宝数据平台的官方博客上有几篇文章介绍了,我就跳开这部分关键内容啦。第一篇打算先写最近挖过的UDF。

         什么是UDF?如果我们写一条这样的HQL:“SELECT SUBSTR(column1, 7), column2 FROM src WHERE column3 < 100”,那么Hive在解析了这条查询语句后,就会将语句中的“substr”和“<"分别封装成对应的UDF对象。大概知道了吧?

         Hive的UDF根据继承的父类可以分为UDF类(指类别,旧)和GenericUDF类,新版本的Hive陆续把部分UDF类的UDF,改写成了GenericUDF类的UDF。udf包中还有一些分工略有不同的UDF,准确的说,叫UDAF,这些是用于GroupBy的聚合函数,其父类也分UDAF和GenericUDAF,但这些不在本篇的讨论范围内。

         先来看看UDF与GenericUDF到底区别在哪:

[java]  view plain copy
  1. public class UDF {  
  2.   
  3.   /** 
  4.    * The resolver to use for method resolution. 
  5.    */  
  6.   private UDFMethodResolver rslv;  
  7.   
  8.   public UDF() {  
  9.     rslv = new DefaultUDFMethodResolver(this.getClass());  
  10.   }  
  11.   
  12.   /** 
  13.    * The constructor with user-provided UDFMethodResolver. 
  14.    */  
  15.   protected UDF(UDFMethodResolver rslv) {  
  16.     this.rslv = rslv;  
  17.   }  
  18.   
  19.   public void setResolver(UDFMethodResolver rslv) {  
  20.     this.rslv = rslv;  
  21.   }  
  22.   
  23.   public UDFMethodResolver getResolver() {  
  24.     return rslv;  
  25.   }  
  26. }  
        上面这个是UDF.java的主要代码,所有UDF类(指类别)的UDF都必须继承自这个父类。可以看到它主要定义了一个UDFMethodResolver类的属性,这个属性的用处待会会详细说明。它在注释中还规定了,所有子类必须实现一个或多个evaluate方法给Hive框架调用,不难猜到,这个evaluate方法正是所有实现类的逻辑部分所在。

[java]  view plain copy
  1. public abstract class GenericUDF {  
  2.   
  3.   /** 
  4.    * A Defered Object allows us to do lazy-evaluation and short-circuiting. 
  5.    * GenericUDF use DeferedObject to pass arguments. 
  6.    */  
  7.   public static interface DeferredObject {  
  8.     Object get() throws HiveException;  
  9.   };  
  10.   
  11.   public GenericUDF() {  
  12.   }  
  13.   
  14.   public abstract ObjectInspector initialize(ObjectInspector[] arguments)  
  15.       throws UDFArgumentException;  
  16.   
  17.   public abstract Object evaluate(DeferredObject[] arguments)  
  18.       throws HiveException;  
  19. }  
        上面这个是Generic.java的主要代码,同样,所有GenericUDF类(亦指类别)的UDF都必须继承自这个父类。它多了一个initialize方法,这个方法对于一个GenericUDF的实例来说只会调用一次(似乎是废话,不然就不叫这个名字了~);它少了UDFMethodResolver类的属性,因为这个属性的用处被initialize方法替代了。GenericUDF类直接定义了evaluate方法,而且不需要子类重载它的参数定义,当然功能还是一样的。

        好,接下来说说为什么要有这样的改进吧:一个是可以接受和返回复杂数据类型了,例如Array什么的结构体类型,而不像UDF类那样只能是int、string之类的基本类型(当然真正代码中定义的是包装过的后缀为Writable的类型,但还是表示基本类型);新的改进可以接受可变长度以及无限长度的参数了,因为可以用数组来表示输入参数了,而不需要像UDF类的实现类那样,要几种参数组合,就得重载几种方法;最重要的改进是可以通过DeferredObject类来实现所谓的”short-circuit“优化(不知道怎么表述~)。

        从SemanticAnalyzer类(查询语句的语义解析类)的代码可以看出,生成的job plan中已经没有UDF类的身影了,所有UDF都是以GenericUDF类的实例出现的。但这并不意味着Hive抛弃了之前的UDF类的实现,而是会通过GenericUDFBridge类实现转接。接着就通过这个GenericUDFBridge来看看UDF.java中的UDFMethodResolver类属性和initialize方法的用处吧。

        从各GenericUDF的实现类可以看出,initialize方法主要是用来处理该UDF的输入参数的类型信息。一般会根据UDF的输入参数的类型(initialize方法的输入参数,各种ObjectInspector对象),生成对应的ObjectInspector实现类对象作为该UDF对象的成员变量,用于evaluate方法的计算过程。这里所谓的生成ObjectInspector对象,其实是获得一个某ObjectInspector实现类对象的引用,因为这些”生成“操作都是通过工厂类来实现的,而这些工厂类保证了这些类都是单一实例……好像又说废话了,还没解释为什么用到了单例模式,那就跑开一下说说各ObjectInspector类的用处吧。

        我们知道,Hive最终跑的job是Hadoop的job,而Hadoop处理的输入数据要么是文本数据,要么是一些序列化后的二进制格式数据,都是没有数据类型的。每当Hive要根据”表“中定义的类型来处理数据的时候,都需要进行相应的类型处理。各种ObjectInspector类就是GenericUDF实现类中干这个活的,不同的ObjectInspector类知道怎么从Object对象(因为源数据没有类型,都是Object)中提取出相应的Writable对象,或者提取出原始的Java类型数据。

        再跑回initialize方法,所以从效率考虑,它们只要是单例的即可。而特别点的GenericUDFBridge类的initialize方法还做了什么呢?因为它要将这一套ObjectInspector的方式用到老的UDF类上,就要做点额外的工作了,其实就是要获取老的UDF对象的结果类型和需要的输入参数类型。直接贴一下代码吧:

[java]  view plain copy
  1. public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {  
  2.   
  3.     udf = (UDF) ReflectionUtils.newInstance(udfClass, null);  
  4.   
  5.     // Resolve for the method based on argument types  
  6.     ArrayList<TypeInfo> argumentTypeInfos = new ArrayList<TypeInfo>(  
  7.         arguments.length);  
  8.     for (ObjectInspector argument : arguments) {  
  9.       argumentTypeInfos.add(TypeInfoUtils  
  10.           .getTypeInfoFromObjectInspector(argument));  
  11.     }  
  12.     udfMethod = udf.getResolver().getEvalMethod(argumentTypeInfos);  
  13.     udfMethod.setAccessible(true);  
  14.   
  15.     // Create parameter converters  
  16.     conversionHelper = new ConversionHelper(udfMethod, arguments);  
  17.   
  18.     // Create the non-deferred realArgument  
  19.     realArguments = new Object[arguments.length];  
  20.   
  21.     // Get the return ObjectInspector.  
  22.     ObjectInspector returnOI = ObjectInspectorFactory  
  23.         .getReflectionObjectInspector(udfMethod.getGenericReturnType(),  
  24.         ObjectInspectorOptions.JAVA);  
  25.   
  26.     return returnOI;  
  27.   }  

从代码可以看到:创建实例,获取实际的参数类型……注意

[java]  view plain copy
  1. udfMethod = udf.getResolver().getEvalMethod(argumentTypeInfos);  

这一行,之前提到的UDFMethodResolver类登场了。跟进它的getEvalMethod方法,发现实际调用了一个FunctionRegistry.getMethodInternal方法:

[java]  view plain copy
  1. return FunctionRegistry.getMethodInternal(udfClass, "evaluate"false,  
  2.         argClasses);  

FunctionRegistry.getMethodInternal的代码里面有这么一段:

[java]  view plain copy
  1.     for (Method m : mlist) {  
  2.       List<TypeInfo> argumentsAccepted = TypeInfoUtils.getParameterTypeInfos(m,  
  3.           argumentsPassed.size());  
  4.       if (argumentsAccepted == null) {  
  5.         // null means the method does not accept number of arguments passed.  
  6.         continue;  
  7.       }  
  8.   
  9.       boolean match = (argumentsAccepted.size() == argumentsPassed.size());  
  10.       int conversionCost = 0;  
  11.   
  12.       for (int i = 0; i < argumentsPassed.size() && match; i++) {  
  13.         int cost = matchCost(argumentsPassed.get(i), argumentsAccepted.get(i),  
  14.             exact);  
  15.         if (cost == -1) {  
  16.           match = false;  
  17.         } else {  
  18.           conversionCost += cost;  
  19.         }  
  20.       }  
  21. ……  
很显然,它是在根据实际的参数类型,在UDF类中寻找一个最合适的evaluate方法。回到initialize方法,接下来的内容就好理解了,依然是生成对应的ObjectInspector对象……

好了,对UDF的挖掘就写到这吧,好久没有写技术博客哩~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值