RCFile在平台的应用场景中多数用于存储需要“长期留存”的数据文件,在我们的实践过程中,RCFile的数据压缩比通常可以达到8 : 1或者10 : 1,特别适用于存储用户通过Hive(MapReduce)分析的结果。目前平台的计算引擎正逐步由Hadoop MapReduce迁移至Spark,存储方面我们依然想利用RCFile的优势,但是具体实践中遇到那么几个“坑”。
 
数据分析师使用PySpark构建Spark分析程序,源数据是按行存储的文本文件(可能有压缩),结果数据为Python list,list的元素类型为tuple,而tuple的元素类型为unicode(Python2,为了保持大家对数据认知的一致性,源数据是文本,我们要求用户的处理结果也为文本),可以看出list实际可以理解为一张表(Table),list的元素tuple为行(Row),tuple的元素为列(Column),因此能够很好的利用RCFile的列式存储特性。
 
RCFile扩展自Hadoop InputFormat、OutputFormat、Writable:
 
org.apache.hadoop.hive.ql.io.RCFileInputFormat
org.apache.hadoop.hive.ql.io.RCFileOutputFormat
 
org.apache.hadoop.hive.serde2.columnar.BytesRefArrayWritable
 
注意:RCFile的使用需要依赖于Hive的Jar。
 
使用RCFileOutputFormat时我们需要处理tuple => BytesRefArrayWritable(Object[] => BytesRefArrayWritable)的数据类型转换,使用RCFileInputFormat时我们需要处理BytesRefArrayWritable => tuple(BytesRefArrayWritable => Object[])的数据类型转换,也就是说我们需要扩展两个Converter:
 
ObjectArrayToBytesRefArrayWritableConverter:用于Object[] => BytesRefArrayWritable的数据类型转换;
BytesRefArrayWritableToObjectArrayConverter:用于BytesRefArrayWritable => Object[]的数据类型转换;
 
注:有关PySpark Converter的相关原理可以参考http://diptech.sinaapp.com/?p=125,在此我们只介绍具体的实现细节。
 
(1)ObjectArrayToBytesRefArrayWritableConverter;
 
 
convert的参数类型为Object[],返回值类型为BytesRefArrayWritable。
 
(2)BytesRefArrayWritableToObjectArrayConverter;
 
 
convert的参数类型为BytesRefArrayWritable,返回值类型为Object[]。
 
1. 模拟数据(用户分析结果),将其以RCFile的形式保存至HDFS;
 
 
我们模拟的数据为三行三列,数据类型均为文本,需要注意的是RCFile在保存数据时需要通过Hadoop Configuration指定“列数”,否则会出现以下异常:
 
 
此外RCFileOutputFormat RecordWriter会丢弃“key”:
 
 
因此“key”可以是任意值,只要兼容Hadoop Writable即可,此处我们将“key”处理为None,并设置keyClass为org.apache.hadoop.io.NullWritable。
 
而且运行上述程序之前,还需要将com.sina.dip.spark.converter.ObjectArrayToBytesRefArrayWritableConverter编译打包为独立的Jar:rcfile.jar,运行命令如下:
 
spark-submit --jars rcfile.jar 1.5.1/examples/app/spark_app_save_data_to_rcfile.py
 
出乎意料,异常信息出现:
 
 
引发异常的代码并不是我们自定义扩展的ObjectArrayToBytesRefArrayWritableConverter,而是RCFileOutputFormat,怎么可能,这不是官方提供的代码么?根据异常堆栈可知,RCFileOutputFormat第79行(不同版本的Hive可能代码行数不同)代码出现空指针异常:
 
 
该行可能引发空指针异常的唯一原因就是outputPath == null,而outputPath的值由方法getWorkOutputPath计算而得:
 
 
其中JobContext.TASK_OUTPUT_DIR的值为mapreduce.task.output.dir。
 
熟悉Hadoop的同学可能已经想到,方法getWorkOutputPath是用来计算Map或Reduce Task临时输出目录的,JobContext.TASK_OUTPUT_DIR属性也是以前缀“mapreduce”开头的,“Spark运行时是不会为该属性设置值的”,所以outputPath == null,那么我们应该如何计算outputPath呢?
 
困惑之余,我们联想到当初调研Spark时是以文本为基础进行功能测试的,也就是说在Spark中使用TextInputFormat、TextOutputFormat是没有任何问题的,果断参考一下TextOutputFormat是如何实现的?
 
 
FileOutputFormat是一个基础类,这意味着我们可以重写RCFileOutputFormat getRecordWriter,使用FileOutputFormat.getTaskOutputPath替换getWorkOutputPath:
 
 
可以看出,重写后的getRecordWriter仅仅是改变了outputPath的计算方式,其它逻辑并没有改变,我们将重写后的类命名为com.sina.dip.spark.output.DipRCFileOutputFormat,并将其一并编译打包为独立的Jar:rcfile.jar。
 
重新修改Spark代码:
 
 
我们作出了两个地方的修改:
 
(1)parallelize numSlices:1,考虑到模拟的数据量比较小,为了便于查看结果,我们将“分区数”设置为1,这样最终仅有一个数据文件;
(2)outputFormatClass:com.sina.dip.spark.output.DipRCFileOutputFormat;
 
再次运行命令:
 
spark-submit --jars rcfile.jar 1.5.1/examples/app/spark_app_save_data_to_rcfile.py
 
程序执行结果之后,我们通过HDFS FS命令查看目录:hdfs://dip.dev.cdh5:8020/user/yurun/rcfile/:
 
 
数据文件已成功生成,为了确认写入的正确性,我们通过Hive RCFileCat命令查看文件:hdfs://dip.dev.cdh5:8020/user/yurun/rcfile/part-00000:
 
 
可见写入文件的数据与我们模拟的数据是一致的。
 
2. 读取上一步写入的数据;
 
 
运行上述程序之前,还需要将com.sina.dip.spark.converter.BytesRefArrayWritableToObjectArrayConverter编译打包为独立的Jar:rcfile.jar,运行命令如下:
 
spark-submit --jars rcfile.jar 1.5.1/examples/app/spark_app_read_data_from_rcfile.py
 
输出结果:
 
 
我们使用Hive原生的RCFileInputFormat,以及我们自己扩展的BytesRefArrayWritableToObjectArrayConverter正确完成了RCFile数据的读取,实际上pair[0]可以理解为“行数”(注意keyClass的设置),通常情况下没有实际意义,可以选择忽略。
 
综上所述,Spark(PySpark)使用RCFile的过程中会遇到三个“坑”:
 
(1)需要重写RCFileOutputFormat getRecordWriter;
(2)需要扩展Converter支持tuple(Object[]) => BytesRefArrayWritable的数据类型转换;
(3)需要扩展Converter支持BytesRefArrayWritable => tuple (Object[])的数据类型转换。 
 

Spark RCFile的那些“坑”的更多相关文章

  1. ALS部署Spark集群入坑记

    [Stage 236:> (0 + 0) / 400]17/12/04 09:45:55 ERROR yarn.ApplicationMaster: User class threw excep ...

  2. spark推测执行的坑

    1.spark推测执行开启 设置 spark.speculation=true即可 2.spark开启推测执行的好处 推测执行是指对于一个Stage里面运行慢的Task,会在其他节点的Executor ...

  3. Spark DateType cast 踩坑

    前言 在平时的 Spark 处理中常常会有把一个如 2012-12-12 这样的 date 类型转换成一个 long 的 Unix time 然后进行计算的需求.下面是一段示例代码: val sche ...

  4. Spark踩坑记——从RDD看集群调度

    [TOC] 前言 在Spark的使用中,性能的调优配置过程中,查阅了很多资料,之前自己总结过两篇小博文Spark踩坑记--初试和Spark踩坑记--数据库(Hbase+Mysql),第一篇概况的归纳了 ...

  5. hive on spark的坑

    原文地址:http://www.cnblogs.com/breg/p/5552342.html 装了一个多星期的hive on spark 遇到了许多坑.还是写一篇随笔,免得以后自己忘记了.同事也给我 ...

  6. [Big Data]从Hadoop到Spark的架构实践

    摘要:本文则主要介绍TalkingData在大数据平台建设过程中,逐渐引入Spark,并且以Hadoop YARN和Spark为基础来构建移动大数据平台的过程. 当下,Spark已经在国内得到了广泛的 ...

  7. [转载] 从Hadoop到Spark的架构实践

    转载自http://www.csdn.net/article/2015-06-08/2824889 http://www.zhihu.com/question/26568496 当下,Spark已经在 ...

  8. 从Hadoop到Spark的架构实践

    当下,Spark已经在国内得到了广泛的认可和支持:2014年,Spark Summit China在北京召开,场面火爆:同年,Spark Meetup在北京.上海.深圳和杭州四个城市举办,其中仅北京就 ...

  9. 利用SparkSQL(java版)将离线数据或实时流数据写入hive的用法及坑点

    1. 通常利用SparkSQL将离线或实时流数据的SparkRDD数据写入Hive,一般有两种方法.第一种是利用org.apache.spark.sql.types.StructType和org.ap ...

随机推荐

  1. [转]JavaScript 的同源策略

    同源策略限制了一个源(origin)中加载文本或脚本与来自其它源(origin)中资源的交互方式. 同源定义 如果两个页面拥有相同的协议(protocol),端口(如果指定),和主机,那么这两个页面就 ...

  2. css hack方法总结

    IE6识别:下划线 _ 和 星号 *IE7识别:星号 *IE6-IE10识别: \9 css hack方式,条件注释法<!--[if IE]>这段文字只在IE浏览器显示<![endi ...

  3. [IO] C# INI文件读写类与源码下载 (转载)

    /// <summary> /// 类说明:INI文件读写类. /// 编 码 人:苏飞 /// 联系方式:361983679 /// 更新网站:[url]http://www.sufei ...

  4. EventBus3 简单使用及注意点

    博客: 安卓之家 微博: 追风917 CSDN: 蒋朋的家 简书: 追风917 # EventBus3 简介 EventBus Android 发布/订阅事件总线,可简化 Activities, Fr ...

  5. c语言学习之基础知识点介绍(五):关系运算式和逻辑运算式

    本节主要说关系运算式和逻辑运算式. 一.关系运算式 1.等于(==):判断左边的表达式是否等于右边的表达式 2.大于(>):判断左边的表达式是否大于右边的表达式 3.大于等于(>=):判断 ...

  6. 如何在swift中实现oc中的分类

    在oc中为了增强已有类的功能,我们经常使用分类.使用分类,我们可以在不破坏原有类的结构的前提下,对原有类进行模块化的扩展. 但是在swift中没有分类这种写法了.相对应的是swift中只有扩展(Ext ...

  7. C# 匿名表达式(Lambda表达式)

    匿名表达式 这次来说说Lambda表达式吧,反正也简单,我也不像其他高手那样强调来强调去,只讲一下方法: 准备条件如下: 第一,匿名表达式必须存在与之对应的委托. 只要存在相对应的委托就可以了.接下来 ...

  8. 系统设计 - 使用面向 iOS 的本机插件扩展

    本文转自:http://www.cnblogs.com/zhwl/archive/2013/07/26/3217155.html 本文细致探讨了 Xcode(以 iOS 设备为目标)中的 PhoneG ...

  9. How To Read a Paper.md

    @ Titile How To Read a Paper.md @ author Keshav, 译 uuplusu   # 1. Intro    1. 读论文重要    2. 没有人教    3. ...

  10. LINUX 添加定时任务

    LINUX 添加定时任务 crontab - l按 i:x 先按 esc然后 敲入 命令 :x*/5 3 * * 0 /root/ v.sh ..重启服务 service crond restart