原文链接:在Spark中自定义Kryo序列化输入输出API

Spark中内置支持两种系列化格式:(1)、Java serialization;(2)、Kryo serialization。在默认情况下,Spark使用的是Java的ObjectOutputStream系列化框架,它支持所有继承java.io.Serializable的类系列化,虽然Java系列化非常灵活,但是它的性能不佳。然而我们可以使用Kryo 库来系列化,它相比Java serialization系列化高效,速度很快(通常比Java快10x),但是它不支持所有的系列化对象,而且要求用户注册类。

  在Spark中,使用Kryo系列化比使用Java系列化更明智。在shuffling和caching大量数据的情况下,使用 Kryo系列化就变得非常重要。

  虽然Kryo支持对RDD的cache和shuffle,但是在Spark中不是内置就显示提供使用Kryo将数据系列化到磁盘中的输入输出API,RDD中的saveAsObjectFile和SparkContext中的objectFile方法仅仅支持使用Java系列化。所以如果我们可以使用Kryo系列化将会变得很棒!

  在这篇文章中,我将讨论如何自定义Kryo系列化输出输出相关API来将数据进行读写到磁盘中。

写数据

  通常,我们使用rdd.saveAsObjectFile API将已经系列化的对象写入到磁盘中。下面的代码将展示如何使用我们自定义的saveAsObjectFile方法将已经使用kryo系列化的对象写入到磁盘中:

1 def saveAsObjectFile[T: ClassTag](rdd: RDD[T], path: String)

这个函数中参数rdd就是我们需要写的数据;path是数据保存的路径。

1 val kryoSerializer = new KryoSerializer(rdd.context.getConf)

  KryoSerializer是Spark内部提供的用于提供操作Kryo的类。在上述代码中,我们创建了KryoSerializer对象,并从rdd.context.getConf中获取传进来的缓存大小。

1 rdd.mapPartitions(iter => iter.grouped(10)
2       .map(_.toArray))
3       .map(splitArray => {}

所有的objectFile 将会在HDFS上保存,我们对RDD中的每个分片进行遍历,然后将他们转换成Byte数组。

1 val kryo = kryoSerializer.newKryo()

  对每个splitArray,我们首先创建了kryo实例,kryo是线程不安全的,所以我们在每个map操作中单独创建。当我们调用 kryoSerializer.newKryo()来创建新的kryo实例,他也会调用我们自定义的KryoRegistrator。

1 //create output stream and plug it to the kryo output
2 val bao = new ByteArrayOutputStream()
3 val output = kryoSerializer.newKryoOutput()
4 output.setOutputStream(bao)
5 kryo.writeClassAndObject(output, splitArray)
6 output.close()

一旦我们拥有kryo实例,我们就可以创建kryo输出对象,然后我们将类信息和对象写入到那个输出对象中。

1 val byteWritable = new BytesWritable(bao.toByteArray)
2       (NullWritable.get(), byteWritable)
3     }).saveAsSequenceFile(path)

  我们在创建byteWritable的时候,包装了bytearray,然后保存成Sequence文件。使用那些代码我们就可以将Kryo对象写入到磁盘中。完整代码如下:

01 /**
02  * User: 过往记忆
03  * Date: 15-04-24
04  * Time: 上午07:24
05  * bolg: http://www.iteblog.com
06  * 本文地址:http://www.iteblog.com/archives/1328
07  * 过往记忆博客,专注于hadoop、hive、spark、shark、flume的技术博客,大量的干货
08  * 过往记忆博客微信公共帐号:iteblog_hadoop
09  */
10  
11   def saveAsObjectFile[T: ClassTag](rdd: RDD[T], path: String) {
12     val kryoSerializer = new KryoSerializer(rdd.context.getConf)
13  
14     rdd.mapPartitions(iter => iter.grouped(10)
15       .map(_.toArray))
16       .map(splitArray => {
17       //initializes kyro and calls your registrator class
18       val kryo = kryoSerializer.newKryo()
19  
20       //convert data to bytes
21       val bao = new ByteArrayOutputStream()
22       val output = kryoSerializer.newKryoOutput()
23       output.setOutputStream(bao)
24       kryo.writeClassAndObject(output, splitArray)
25       output.close()
26  
27       // We are ignoring key field of sequence file
28       val byteWritable = new BytesWritable(bao.toByteArray)
29       (NullWritable.get(), byteWritable)
30     }).saveAsSequenceFile(path)
31   }

读数据

  光有写没有读对我们来说仍然不完美。通常我们使用sparkContext中的objectFile API从磁盘中读取数据,这里我们使用自定义的objectFile API来读取Kryo对象文件。

01 def objectFile[T](sc: SparkContext, path: String, minPartitions: Int = 1)
02     (implicit ct: ClassTag[T]) = {
03     val kryoSerializer = new KryoSerializer(sc.getConf)
04     sc.sequenceFile(path, classOf[NullWritable], classOf[BytesWritable],
05        minPartitions)
06        .flatMap(x => {
07        val kryo = kryoSerializer.newKryo()
08        val input = new Input()
09        input.setBuffer(x._2.getBytes)
10        val data = kryo.readClassAndObject(input)
11        val dataObject = data.asInstanceOf[Array[T]]
12        dataObject
13     })
14   }

上面的步骤和写的步骤很类似,只不过这里我们使用的是input,而不是output。我们从BytesWritable中读取bytes数据,然后使用readClassAndObject API反序列化数据。

如何使用

  下面例子使用上面介绍的两个方法来系列化和反序列化Person对象:

01 /**
02  * User: 过往记忆
03  * Date: 15-04-24
04  * Time: 上午07:24
05  * bolg: http://www.iteblog.com
06  * 本文地址:http://www.iteblog.com/archives/1328
07  * 过往记忆博客,专注于hadoop、hive、spark、shark、flume的技术博客,大量的干货
08  * 过往记忆博客微信公共帐号:iteblog_hadoop
09  */
10  
11 // user defined class that need to serialized
12   class Person(val name: String)
13  
14  def main(args: Array[String]) {
15  
16     if (args.length < 1) {
17       println("Please provide output path")
18       return
19     }
20     val outputPath = args(0)
21  
22     val conf = new SparkConf().setMaster("local").setAppName("kryoexample")
23     conf.set("spark.serializer""org.apache.spark.serializer.KryoSerializer")
24     val sc = new SparkContext(conf)
25  
26     //create some dummy data
27     val personList = 1 to 10000 map (value =new Person(value + ""))
28     val personRDD = sc.makeRDD(personList)
29  
30     saveAsObjectFile(personRDD, outputPath)
31     val rdd = objectFile[Person](sc, outputPath)
32     println(rdd.map(person => person.name).collect().toList)
33   }

在Spark中自定义Kryo序列化输入输出API(转)的更多相关文章

  1. Spark:将RDD[List[String,List[Person]]]中的List[Person]通过spark api保存为hdfs文件时一直出现not serializable task,没办法找到"spark自定义Kryo序列化输入输出API"

    声明:本文转自<在Spark中自定义Kryo序列化输入输出API>   在Spark中内置支持两种系列化格式:(1).Java serialization:(2).Kryo seriali ...

  2. 在Spark中使用Kryo序列化

    spark序列化  对于优化<网络性能>极为重要,将RDD以序列化格式来保存减少内存占用. spark.serializer=org.apache.spark.serializer.Jav ...

  3. Spark中持久化和序列化学习

    一.cache和persisit的对比 -rw-r--r--@ 1 hadoop staff 68M 5 17 07:04 access.log    cache/persitence是 laz ...

  4. Spark中自定义累加器

    通过继承AccumulatorV2可以实现自定义累加器. 官方案例可参考:http://spark.apache.org/docs/latest/rdd-programming-guide.html# ...

  5. jackson中自定义处理序列化和反序列化

    http://jackyrong.iteye.com/blog/2005323 ********************************************** 对于一直用gson的人来说 ...

  6. Spark中自定义累加器Accumulator

    1. 自定义累加器 自定义累加器需要继承AccumulatorParam,实现addInPlace和zero方法. 例1:实现Long类型的累加器 object LongAccumulatorPara ...

  7. Spark 中的机器学习库及示例

    MLlib 是 Spark 的机器学习库,旨在简化机器学习的工程实践工作,并方便扩展到更大规模.MLlib 由一些通用的学习算法和工具组成,包括分类.回归.聚类.协同过滤.降维等,同时还包括底层的优化 ...

  8. 【Spark调优】Kryo序列化

    [Java序列化与反序列化] Java序列化是指把Java对象转换为字节序列的过程:而Java反序列化是指把字节序列恢复为Java对象的过程.序列化使用场景:1.数据的持久化,通过序列化可以把数据永久 ...

  9. spark 中的RDD编程 -以下基于Java api

    1.RDD介绍:     RDD,弹性分布式数据集,即分布式的元素集合.在spark中,对所有数据的操作不外乎是创建RDD.转化已有的RDD以及调用RDD操作进行求值.在这一切的背后,Spark会自动 ...

随机推荐

  1. 【LOJ】#2085. 「NOI2016」循环之美

    题解 我们要求的其实是这个东西= = \(\sum_{i = 1}^{n}\sum_{j = 1}^{n}[(i,j) == 1][(j,k) == 1]\) 然后变一下形 \(\sum_{j = 1 ...

  2. Windows搭建python开发环境[一]

    首先需要去python的官网下载环境.鼠标移动到Downloads的tab上,在这里可以下载. python的环境还是很人性化的,没有那么多罗里吧嗦的配置什么的,下载好以后直接无脑next就行了,直到 ...

  3. JavaScript最全编码规范

    转载: JavaScript最全编码规范 类型 ●基本类型:访问基本类型时,应该直接操作类型值 ●string ●number ●boolean ●null ●undefined var foo = ...

  4. github 笔记(一)

    笔记预留 0. echo "# Try" >> README.md git init git add README.md git commit -m "fir ...

  5. Java中的强引用,软引用,弱引用

    作者:winterSunshine链接:https://www.zhihu.com/question/37401125/answer/100981172来源:知乎著作权归作者所有.商业转载请联系作者获 ...

  6. PHP 笔记——Web页面交互

    一.客户端数据提交方法 客户端浏览器的数据通常使用 GET.POST 方式提交到服务器. 1.GET方式 GET方式指直接在URL中提供上传数据或者通过表单采用GET方式上传. http://url? ...

  7. HDU 3404 Switch lights 博弈论 nim积

    http://acm.hdu.edu.cn/showproblem.php?pid=3404 题目 http://www.doc88.com/p-5098170314707.html 论文 nim积在 ...

  8. 二、python的逻辑运算与数据类型

    .python的逻辑运算符 数学运算符 加:+   减:-  乘:*  除:/  取余:% 关系运算符 等于: ==  不等于: != 小于:< 大于:>     大于等于: >=  ...

  9. undefined null 各种值比较(面试题)

    undefined和null与任何有意义的值比较返回的都是false,但是null与undefined之间互相比较返回的是true. console.log(null == false); //fal ...

  10. Cwrsync_rsync windows_windows下的rsync

    1.官网已不允许免费下载cwrsync的server了,我就先给出下载地址: http://files.cnblogs.com/files/assassin1994/cwRsync_4.0.5_ser ...