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
得到的类型。