Spark 实现自定义对象sequenceFile方式存储,读写示例(scala编写)

 1 package com.fuge.bigdata.datahub.analysis
 2 
 3 import java.io.{DataInput, DataOutput}
 4 
 5 import com.fuge.bigdata.tools.common.utils.SparkUtils
 6 import org.apache.hadoop.io.{NullWritable, WritableComparable}
 7 import org.apache.spark.SparkContext
 8 
 9 /**
10   * Created by chen xiang on 18-6-13.
11   * 一个使用SequenceFile进行存储读取的使用示例
12   */
13 object SequenceFileUsage {
14   def main(args: Array[String]): Unit = {
15 
16     require(args.length == 1)
17     // 构建SparkContext对象,封装过,单独运行,自行修改后定义
18     val sc = new SparkContext(SparkUtils.getSparkConf("SequenceFileUsage"))
19 
20     // 获取路径参数
21     val path = args(0).trim
22 
23     // 定义测试数据
24     val studentList = List(Student("01", "abc"), Student("02", "baby"), Student("03", "xiang"))
25 
26     // 序列化测试数据到RDD,并写入到bos
27     sc.parallelize(studentList)
28       .repartition(1)
29       // 以NullWritable 为key,构建kv结构.SequenceFile需要kv结构才能存储,NullWritable不占存储
30       .map(NullWritable.get() -> _)
31 // 压缩参数可选用
32       .saveAsSequenceFile(s"$path", Option(classOf[GzipCodec]))
33 
34     // 读取刚才写入的数据
35     val studentRdd = sc.sequenceFile(s"$path/part-*", classOf[NullWritable], classOf[Student])
36       .map {
37         // 读取数据,并且重新赋值对象
38         case (_, y) => Student(y.id, y.name)
39       }
40       .persist()
41 
42     studentRdd
43       .foreach(x => println("count: " + x.id + "\t" + x.name))
44   }
45 }
46 
47 case class Student(var id: String, var name: String) extends WritableComparable[Student] {
48   /**
49     * 重写无参构造函数,用于反序列化时的反射操作
50     */
51   def this() {
52     this("", "")
53   }
54 
55   /**
56     * 继承Comparable接口需要实现的方法,用于比较两个对象的大小
57     */
58   override def compareTo(o: Student): Int = {
59     var cmp = id compareTo o.id
60     if (cmp == 0) {
61       cmp = name compareTo o.name
62     }
63     cmp
64   }
65 
66   /**
67     * 继承Writable接口需要实现的方法-反序列化读取结果,并且赋值到对象字段
68     * 注意要和write的顺序一致
69     */
70   override def readFields(in: DataInput): Unit = {
71     name = in.readUTF()
72     id = in.readUTF()
73     println("count: " + "\t id = " + id + "\t name = " + name)
74   }
75 
76   /**
77     * 继承Writable接口需要实现的方法-序列化写操作,将对象字段值写入序列化
78     * 注意要和readFields的顺序一致
79     */
80   override def write(out: DataOutput): Unit = {
81     out.writeUTF(id)
82     out.writeUTF(name)
83   }
84 }

 

补充:
1. 自定义的类需要进行序列化,必须都要实现Writable,一般情况下采用实现WritableComparable的方式,并且实现comparaTo,readFields, write方法,并且提供一个无参构造函数
2. readFields和write方法,里面字段的顺序要保持一致
3. 遇到集合类型,序列化时需要先将集合长度写进去,然后再挨个写集合数据
4. 遇到集合类型,反序列化时需要先读取集合的长度,然后接收数据,如果集合数据类型是自定义类型,还需要先实例化一个无参构造,然后赋值。
5. SequenceFile需要使用KV结构才能调用存储,可以使用一个NullWritable来占位,上诉例子中的K值就是使用的NullWritable进行的
6. sequenceFile序列化后占用的存储空间比较大,有需要的话,可以在存储的时候加上压缩算法,具体使用方式可以见上诉的例子

posted @ 2018-06-13 15:50  时光舟  阅读(3289)  评论(1编辑  收藏  举报