Hive UDAF开发详解
说明
udfa是hive中用户自定义的聚集函数,hive内置UDAF函数包括有sum()与count(),UDAF实现有简单与通用两种方式,简单UDAF因为使用Java反射导致性能损失,而且有些特性不能使用,已经被弃用了;在这篇博文中我们将关注Hive中自定义聚类函数-GenericUDAF,UDAF开发主要涉及到以下两个抽象类:
- org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver
- org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator
源码链接
示例数据准备
首先先创建一张包含示例数据的表:people,该表只有name一列,该列中包含了一个或多个名字,该表数据保存在people.txt文件中。
- ~$ cat ./people.txt
- John Smith
- John and Ann White
- Ted Green
- Dorothy
把该文件上载到hdfs目录/user/matthew/people中:
- hadoop fs -mkdir people
- hadoop fs -put ./people.txt people
下面要创建hive外部表,在hive shell中执行
- CREATE EXTERNAL TABLE people (name string)
- ROW FORMAT DELIMITED FIELDS
- TERMINATED BY '\t'
- ESCAPED BY ''
- LINES TERMINATED BY '\n'
- STORED AS TEXTFILE
- LOCATION '/user/matthew/people';
相关抽象类介绍
- org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver
- org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator
为了更好理解上述抽象类的API,要记住hive只是mapreduce函数,只不过hive已经帮助我们写好并隐藏mapreduce,向上提供简洁的sql函数,所以我们要结合Mapper、Combiner与Reducer来帮助我们理解这个函数。要记住在Hadoop集群中有若干台机器,在不同的机器上Mapper与Reducer任务独立运行。
所以大体上来说,这个UDAF函数读取数据(mapper),聚集一堆mapper输出到部分聚集结果(combiner),并且最终创建一个最终的聚集结果(reducer)。因为我们跨域多个combiner进行聚集,所以我们需要保存部分聚集结果。
AbstractGenericUDAFResolver
Resolver很简单,要覆盖实现下面方法,该方法会根据sql传人的参数数据格式指定调用哪个Evaluator进行处理。
- <span style="background-color: rgb(255, 255, 255);"><span style="font-size:14px;">public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters) throws SemanticException;</span></span>
GenericUDAFEvaluator
UDAF逻辑处理主要发生在Evaluator中,要实现该抽象类的几个方法。
在理解Evaluator之前,必须先理解objectInspector接口与GenericUDAFEvaluator中的内部类Model。
ObjectInspector
Model
Model代表了UDAF在mapreduce的各个阶段。
- public static enum Mode {
- /**
- * PARTIAL1: 这个是mapreduce的map阶段:从原始数据到部分数据聚合
- * 将会调用iterate()和terminatePartial()
- */
- PARTIAL1,
- /**
- * PARTIAL2: 这个是mapreduce的map端的Combiner阶段,负责在map端合并map的数据::从部分数据聚合到部分数据聚合:
- * 将会调用merge() 和 terminatePartial()
- */
- PARTIAL2,
- /**
- * FINAL: mapreduce的reduce阶段:从部分数据的聚合到完全聚合
- * 将会调用merge()和terminate()
- */
- FINAL,
- /**
- * COMPLETE: 如果出现了这个阶段,表示mapreduce只有map,没有reduce,所以map端就直接出结果了:从原始数据直接到完全聚合
- * 将会调用 iterate()和terminate()
- */
- COMPLETE
- };
一般情况下,完整的UDAF逻辑是一个mapreduce过程,如果有mapper和reducer,就会经历PARTIAL1(mapper),FINAL(reducer),如果还有combiner,那就会经历PARTIAL1(mapper),PARTIAL2(combiner),FINAL(reducer)。
而有一些情况下的mapreduce,只有mapper,而没有reducer,所以就会只有COMPLETE阶段,这个阶段直接输入原始数据,出结果。
GenericUDAFEvaluator的方法
- // 确定各个阶段输入输出参数的数据格式ObjectInspectors
- public ObjectInspector init(Mode m, ObjectInspector[] parameters) throws HiveException;
- // 保存数据聚集结果的类
- abstract AggregationBuffer getNewAggregationBuffer() throws HiveException;
- // 重置聚集结果
- public void reset(AggregationBuffer agg) throws HiveException;
- // map阶段,迭代处理输入sql传过来的列数据
- public void iterate(AggregationBuffer agg, Object[] parameters) throws HiveException;
- // map与combiner结束返回结果,得到部分数据聚集结果
- public Object terminatePartial(AggregationBuffer agg) throws HiveException;
- // combiner合并map返回的结果,还有reducer合并mapper或combiner返回的结果。
- public void merge(AggregationBuffer agg, Object partial) throws HiveException;
- // reducer阶段,输出最终结果
- public Object terminate(AggregationBuffer agg) throws HiveException;
图解Model与Evaluator关系
Model各阶段对应Evaluator方法调用
Evaluator各个阶段下处理mapreduce流程
实例
下面将讲述一个聚集函数UDAF的实例,我们将计算people这张表中的name列字母的个数。
下面的函数代码是计算指定列中字符的总数(包括空格)
代码
- @Description(name = "letters", value = "_FUNC_(expr) - 返回该列中所有字符串的字符总数")
- public class TotalNumOfLettersGenericUDAF extends AbstractGenericUDAFResolver {
- @Override
- public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters)
- throws SemanticException {
- if (parameters.length != 1) {
- throw new UDFArgumentTypeException(parameters.length - 1,
- "Exactly one argument is expected.");
- }
- ObjectInspector oi = TypeInfoUtils.getStandardJavaObjectInspectorFromTypeInfo(parameters[0]);
- if (oi.getCategory() != ObjectInspector.Category.PRIMITIVE){
- throw new UDFArgumentTypeException(0,
- "Argument must be PRIMITIVE, but "
- + oi.getCategory().name()
- + " was passed.");
- }
- PrimitiveObjectInspector inputOI = (PrimitiveObjectInspector) oi;
- if (inputOI.getPrimitiveCategory() != PrimitiveObjectInspector.PrimitiveCategory.STRING){
- throw new UDFArgumentTypeException(0,
- "Argument must be String, but "
- + inputOI.getPrimitiveCategory().name()
- + " was passed.");
- }
- return new TotalNumOfLettersEvaluator();
- }
- public static class TotalNumOfLettersEvaluator extends GenericUDAFEvaluator {
- PrimitiveObjectInspector inputOI;
- ObjectInspector outputOI;
- PrimitiveObjectInspector integerOI;
- int total = 0;
- @Override
- public ObjectInspector init(Mode m, ObjectInspector[] parameters)
- throws HiveException {
- assert (parameters.length == 1);
- super.init(m, parameters);
- //map阶段读取sql列,输入为String基础数据格式
- if (m == Mode.PARTIAL1 || m == Mode.COMPLETE) {
- inputOI = (PrimitiveObjectInspector) parameters[0];
- } else {
- //其余阶段,输入为Integer基础数据格式
- integerOI = (PrimitiveObjectInspector) parameters[0];
- }
- // 指定各个阶段输出数据格式都为Integer类型
- outputOI = ObjectInspectorFactory.getReflectionObjectInspector(Integer.class,
- ObjectInspectorOptions.JAVA);
- return outputOI;
- }
- /**
- * 存储当前字符总数的类
- */
- static class LetterSumAgg implements AggregationBuffer {
- int sum = 0;
- void add(int num){
- sum += num;
- }
- }
- @Override
- public AggregationBuffer getNewAggregationBuffer() throws HiveException {
- LetterSumAgg result = new LetterSumAgg();
- return result;
- }
- @Override
- public void reset(AggregationBuffer agg) throws HiveException {
- LetterSumAgg myagg = new LetterSumAgg();
- }
- private boolean warned = false;
- @Override
- public void iterate(AggregationBuffer agg, Object[] parameters)
- throws HiveException {
- assert (parameters.length == 1);
- if (parameters[0] != null) {
- LetterSumAgg myagg = (LetterSumAgg) agg;
- Object p1 = ((PrimitiveObjectInspector) inputOI).getPrimitiveJavaObject(parameters[0]);
- myagg.add(String.valueOf(p1).length());
- }
- }
- @Override
- public Object terminatePartial(AggregationBuffer agg) throws HiveException {
- LetterSumAgg myagg = (LetterSumAgg) agg;
- total += myagg.sum;
- return total;
- }
- @Override
- public void merge(AggregationBuffer agg, Object partial)
- throws HiveException {
- if (partial != null) {
- LetterSumAgg myagg1 = (LetterSumAgg) agg;
- Integer partialSum = (Integer) integerOI.getPrimitiveJavaObject(partial);
- LetterSumAgg myagg2 = new LetterSumAgg();
- myagg2.add(partialSum);
- myagg1.add(myagg2.sum);
- }
- }
- @Override
- public Object terminate(AggregationBuffer agg) throws HiveException {
- LetterSumAgg myagg = (LetterSumAgg) agg;
- total = myagg.sum;
- return myagg.sum;
- }
- }
- }
代码说明
这里有一些关于combiner的资源,Philippe Adjiman 讲得不错。
AggregationBuffer
允许我们保存中间结果,通过定义我们的buffer,我们可以处理任何格式的数据,在代码例子中字符总数保存在AggregationBuffer
。
- /**
- * 保存当前字符总数的类
- */
- static class LetterSumAgg implements AggregationBuffer {
- int sum = 0;
- void add(int num){
- sum += num;
- }
- }
这意味着UDAF在不同的mapreduce阶段会接收到不同的输入。Iterate读取我们表中的一行(或者准确来说是表),然后输出其他数据格式的聚集结果。
artialAggregation
合并这些聚集结果到另外相同格式的新的聚集结果,然后最终的reducer取得这些聚集结果然后输出最终结果(该结果或许与接收数据的格式不一致)。
在init()方法中我们指定输入为string,结果输出格式为integer,还有,部分聚集结果输出格式为integer(保存在aggregation buffer中);terminate()
与
terminatePartial()
两者输出一个
integer
。
- // init方法中根据不同的mode指定输出数据的格式objectinspector
- if (m == Mode.PARTIAL1 || m == Mode.COMPLETE) {
- inputOI = (PrimitiveObjectInspector) parameters[0];
- } else {
- integerOI = (PrimitiveObjectInspector) parameters[0];
- }
- // 不同model阶段的输出数据格式
- outputOI = ObjectInspectorFactory.getReflectionObjectInspector(Integer.class,
- ObjectInspectorOptions.JAVA);
iterate()
函数读取到每行中列的字符串,计算与保存该字符串的长度
- public void iterate(AggregationBuffer agg, Object[] parameters)
- throws HiveException {
- ...
- Object p1 = ((PrimitiveObjectInspector) inputOI).getPrimitiveJavaObject(parameters[0]);
- myagg.add(String.valueOf(p1).length());
- }
- }
Merge函数增加部分聚集总数到AggregationBuffer
- public void merge(AggregationBuffer agg, Object partial)
- throws HiveException {
- if (partial != null) {
- LetterSumAgg myagg1 = (LetterSumAgg) agg;
- Integer partialSum = (Integer) integerOI.getPrimitiveJavaObject(partial);
- LetterSumAgg myagg2 = new LetterSumAgg();
- myagg2.add(partialSum);
- myagg1.add(myagg2.sum);
- }
- }
Terminate()函数返回AggregationBuffer中的内容,这里产生了最终结果。
- public Object terminate(AggregationBuffer agg) throws HiveException {
- LetterSumAgg myagg = (LetterSumAgg) agg;
- total = myagg.sum;
- return myagg.sum;
- }
使用自定义函数
- ADD JAR ./hive-extension-examples-master/target/hive-extensions-1.0-SNAPSHOT-jar-with-dependencies.jar;
- CREATE TEMPORARY FUNCTION letters as 'com.matthewrathbone.example.TotalNumOfLettersGenericUDAF';
- SELECT letters(name) FROM people;
- OK
- 44
- Time taken: 20.688 seconds
资料参考
Hive UDAF开发详解的更多相关文章
- Hive UDAF开发之同时计算最大值与最小值
卷首语 前一篇文章hive UDAF开发入门和运行过程详解(转)里面讲过UDAF的开发过程,其中说到如果要深入理解UDAF的执行,可以看看求平均值的UDF的源码 本人在看完源码后,也还是没能十分理解里 ...
- Hadoop Hive sql语法详解
Hadoop Hive sql语法详解 Hive 是基于Hadoop 构建的一套数据仓库分析系统,它提供了丰富的SQL查询方式来分析存储在Hadoop 分布式文件系统中的数据,可以将结构 化的数据文件 ...
- 大数据Hadoop核心架构HDFS+MapReduce+Hbase+Hive内部机理详解
微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...
- Hadoop核心架构HDFS+MapReduce+Hbase+Hive内部机理详解
转自:http://blog.csdn.net/iamdll/article/details/20998035 分类: 分布式 2014-03-11 10:31 156人阅读 评论(0) 收藏 举报 ...
- EasyPR--开发详解(6)SVM开发详解
在前面的几篇文章中,我们介绍了EasyPR中车牌定位模块的相关内容.本文开始分析车牌定位模块后续步骤的车牌判断模块.车牌判断模块是EasyPR中的基于机器学习模型的一个模块,这个模型就是作者前文中从机 ...
- 基于H5的微信支付开发详解
这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
- ****基于H5的微信支付开发详解[转]
这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
- 【转发】NPAPI开发详解,Windows版
NPAPI开发详解,Windows版 9 jiaofeng601, +479 9人支持,来自Meteor.猪爪.hanyuxinting更多 .是非黑白 .Yuan Xulei.hyolin.Andy ...
- 热烈祝贺华清远见《ARM处理器开发详解》第2版正式出版
2014年6月,由华清远见研发中心组织多名业 内顶尖讲师编写的<ARM处理器开发详解>一书正式出版.本书以S5PV210处理器为平台,详细介绍了嵌入式系统开发的各个主要环节,并注重实践,辅 ...
随机推荐
- oracle报错:ORA-01658(转自52斋347)
在oracle里创建表,报出错:ORA-01658: 无法为表空间space中的段创建 INITIAL 区:或者: ORA-01658: unable to create INITIAL extent ...
- vue2.0 vue-cli+webpack使用less和scss的说明
config 目录下好像都不需要相关配置,但是package.json里面 使用less cnpm install --save-dev less less-loader //下面不需要配置,可省略 ...
- css 03
DIV+CSS盒子模型 一.盒子模型css height width padding 内边距 margin 外边距 border 1.margin 外边距 margin-top:15px; marg ...
- Apache Spark 2.2.0 中文文档 - GraphX Programming Guide | ApacheCN
GraphX Programming Guide 概述 入门 属性 Graph 示例属性 Graph Graph 运算符 运算符的汇总表 Property 运算符 Structural 运算符 Joi ...
- html5 app开发实例 Ajax跨域访问C# webservices服务
通过几天的研究效果,如果在vs2010工具上通过webservice还是比较简单的,毕竟是一个项目. 如果您想通过HTML5 做出来的移动APP去访问c#做出来的webservice,那么就没那么简单 ...
- I-129表
http://www.uscis.gov/i-129 移民局使用的非移民性质的工作身份申请表(I-129)表格将于下月底正式作废,4月30日之后,公民和移民服务局只接受新的I-129表格. 据了解,非 ...
- UOJ#126【NOI2013】快餐店
[NOI2013]快餐店 链接:http://uoj.ac/problem/126 YY了一个线段树+类旋转卡壳的算法.骗了55分.还比不上$O(n^2)$暴力T^T 题目实际上是要找一条链的两个端点 ...
- java Vamei快速教程03 构造器和方法重载
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在方法与数据成员中,我们提到,Java中的对象在创建的时候会初始化(initial ...
- sql常识
1.UNION与UNION ALL的区别UNION去重且排序UNION ALL不去重不排序2.sql语句or与union all的执行效率比较:union all>union> in &g ...
- xtarbackup恢复
xbstream -x < ynhw-mysql-slave.01.mysql.prod.sg_fullbak_20180326134255.xbstream -C /data/mysql cd ...