Hive UDF的开发,实习的第一个任务,以前也写过,记录点东西。
UDF
Hive UDF开发可以继承UDF或者GenericUDF。总结以下几点:
- UDF比较简单,只需要实现一个
evaluate方法,Hive会根据实际UDF传入的参数来调用对应的evaluate方法,这也正是我们在源码中看到有些UDF的evaluate方法重载了很多次来支持不同类型的输入。 - UDF的返回值一定要是
writable的,比如Text,IntegerWritable等。 - GenericUDF需要实现三个方法,
initialize、evaluate和getDisplayString。evaluate方法和UDF的evaluate是一个含义,对每行的数据做处理。getDisplayString一定不能返回null。initialize只在每次调用开始时执行一次,主要用于获得参数类型,检查参数等。从源码的内置GenericUDF中可以看出,主要是要掌握对ObjectInspector及其相关的几个类。下面简单介绍下常用的方法,以及参数从Hive类型到Java标准类型的转换。
ObjectInspector
- 获取参数类型
ObjectInspector.getCategory(); - 获取converter
ObjectInspectorConverters.getConverter(arguments, PrimitiveObjectInspectorFactory.writableXXXObjectInspector); - 类型转换
converter.convert(argument.get()) - 基本类型的
ObjectInspector的操作,强制转换即可((PrimitiveObjectInspector) argument).getPrimitiveCategory() - 相关工厂类和工具类的使用,主要是
ObjectInspectorFactory和PrimitiveObjectInspectorUtils,例如类型转换也可以用PrimitiveObjectInspectorUtils.getInt(object, inputOI)
UDTF
UDTF是一对多的生成函数,用的不多,写起来也很简单。继承GenericUDTF包括以下几个函数:initialize、process、close、toString,基本上都是字面意思。处理逻辑主要在process,和GenericUDF一样,输入是一行,但是UDTF可以通过循环forward来生成多行数据,forward的是一个Object[],里面是writable的元素。
UDAF
UDAF是最复杂的,UDAF通常包括两个静态内部类,外部类继承自AbstractGenericUDAFResolver只需要实现一个方法getEvaluator,getEvaluator返回一个GenericUDAFEvaluator,也就是第一个内部类需要继承实现的GenericUDAFEvaluator,包括init、getNewAggregationBuffer、reset、iterate、terminatePartial、merge、terminate,以及另一个内部类AggregationBuffer,也就是getNewAggregationBuffer返回的对象。这些方法主要对应MapReduce的不同阶段,这里不详细介绍,主要记录遇到的坑,包括四点
AggregationBuffer的初始化顺序,或者说调用顺序不直观,有的初始化工作需要在运行时做。outputOI = ObjectInspectorUtils.getStandardObjectInspector(inputOI),这条语句可以传入第二个参数JAVA等,默认是由Hive来判断,这里一定要注意前后一致,不然很容易出现类似与Int和IntegerWritable之间转换的问题。ObjectInspectorUtils.compare(maxagg.o, outputOI, object, inputOI);其中outputOI通常是由inputOI得到的,见第2点,具体可参考内置的Max函数。- struct的结果可以这样构造
ObjectInspectorFactory.getStandardStructObjectInspector(fName, fieldOIs),调用时可以转换成LazyBinaryStruct来操作,objects = ((LazyBinaryStruct) partial).getFieldsAsList();
其他
这几天开发了几十个UDF大部分都是与时间处理有关的,这里记录下Hive和Java关于时间处理的一些问题。
- Hive在许多新版本,例如1.2、1.3、2.0中加入了一些新的UDF是老版本所没有的,可以迁移过来,但是也要迁移相关的一些类,有的可能需要新版本的GenericUDF
- 日期处理通常是由一个统一的模块来解析不同含义变量代表的时间,例如
String、Date、Timestamp甚至也可以用int来表示时间。通常用SimpleDateFormat来parse这些参数,需要注意format的格式,可以通过调整顺序等方式,达到最优的parse效果。 - Hive里不像MySQL区分
Date,DateTime,以及Time。需要自行处理。 - Java8对时间有了很好的支持,包括1.8以前支持的Date只能精确到毫秒。Java8的time包可以更加精确,且对Time,Date等做了区分。
Joda和310这些第三方库对时间处理支持很好,提供了许多实用的函数。比Date和Calendar好用。- 滑动窗口取最值问题:双端队列维护递增序列。
- UDF的泛型要么是参数重载,要么是
switch-case来确定参数的ObjectInspector得到的类型。