简单聊聊Kryo serialization

来源:互联网 发布:谷歌搜索引擎优化指南 编辑:程序博客网 时间:2024/05/18 03:02
Spark默认使用Java的序列化和反序列化机制,即使用Java的ObjectOutputStream框架,支持所有继承于java.io.Serializable对象的序列化。但在Java序列化时,并没有对序列化的byte做任何处理,序列化的元素也太多了:包括开发、类描述、字段值、类的版本、元数据、字段描述和字段值等信息,造成Java序列化速度慢,序列化后的byte数组大。

因此,Spark又提供了另一种高效的序列化方法:Kryo。Kryo是一个快速高效的Java对象图形序列化框架,原生支持Java,且在Java的序列化上甚至优于Google著名的序列化框架protobuf。那,Kryo为什么那么快呢?

Kryo为了保证序列化的高效性,不会序列化对象的field描述信息,且对int和long使用变长存储。这样,序列化后的对象大小就比较小,且由于Kryo只会在第一次序列化类时才去加载类,后续直接保存了类的信息,整体的序列化速度就会提升很明显。详细研究的话,读者可以对比上述两种序列化后的byte内容。

好了,简单了解了Kryo的基本信息后,我们来看看如何在Spark中使用Kryo serialization。
Spark中使用Kryo serialization

如下示例,在初始化SparkContext之前,需指定使用Kryo serialization。

object KryoSparkTest {  def main(args: Array[String]) {    val conf = new SparkConf()    conf.setMaster("local")    conf.setAppName("KryoSparkTest")    // 指定使用Kryo序列化    conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")    conf.set("spark.kryo.registrator", "com.gjl.kryotest.MyRegistrator")    val sc = new SparkContext(conf)    val rdd = sc.textFile("src/main/resources/hello.txt")    val srcMap = rdd.map(f => {      val line = f.split(",")      val hello = new Hello      hello.setA(line(0).toInt)      hello.setId(line(1).toLong)      hello.setWords(line(2))      hello    })    srcMap.persist(StorageLevel.MEMORY_AND_DISK_SER)    println(srcMap.count())  }}其中,MyRegistrator的定义如下:public class MyRegistrator implements KryoRegistrator {    @Override    public void registerClasses(Kryo kryo) {        kryo.register(Hello.class);    }}Hello类的定义如下:public class Hello implements Serializable {    private int a;    private long id;    private String words;    public int getA() {        return a;    }    public void setA(int a) {        this.a = a;    }    public long getId() {        return id;    }    public void setId(long id) {        this.id = id;    }    public String getWords() {        return words;    }    public void setWords(String words) {        this.words = words;    }}运行KryoSparkTest,查看输出日志,如:16/04/05 10:18:13 INFO MemoryStore: Block rdd_2_0 stored as bytes in memory (estimated size 37.0 B, free 118.8 KB)如果不使用Kryo serialization,运行结果为:16/04/05 10:46:00 INFO MemoryStore: Block rdd_2_0 stored as bytes in memory (estimated size 160.0 B, free 118.9 KB)从上述运行结果中可以看出Kryo serialization的存储优势。由于hello.txt文本数比较小,仅说明问题,不做速度压测验证。Scala如何使用Kryo?Chill是Kryo serialization库的Scala扩展。为什么使用Chill,可参加官网说明:    Scala classes often have a number of properties that distinguish them from usual Java classes. Often scala classes are immutable, and thus have no zero argument constructor. Secondly, object in scala is a singleton that needs to be carefully serialized. Additionally, scala classes often have synthetic (compiler generated) fields that need to be serialized, and by default Kryo does not serialize those.下面是一个简单的使用示例:object ChillTest {  def main(args: Array[String]) {    val src = Source.fromFile("src/main/resources/hello.txt").getLines().map(f => {      val line = f.split(",")      val hello = new Hello      hello.setA(line(0).toInt)      hello.setId(line(1).toLong)      hello.setWords(line(2))      hello    }).toList    val instantiator = new ScalaKryoInstantiator    instantiator.setRegistrationRequired(false)    val kryo = instantiator.newKryo()    val baos = new ByteArrayOutputStream()    val output = new Output(baos, 4096)    kryo.writeObject(output, src)    val input = new Input(baos.toByteArray)    val deser = kryo.readObject(input, classOf[List[Hello]])    assert(deser.size == 3)  }}

结语

前文讲过,Kryo为了做到高效序列化,并不序列化对象的field信息,因此,在使用Kryo serialization之前要记得自己加载需要序列化的类。如果Spark中设置了Kryo序列化,但忘记注册类了,运行时会直接报错。另外,序列化类的ID信息也要合理设计,兼容版本和连续性。

另外,本文对序列化的底层技术细节没有过多涉及,也没有详细解析Kryo serialization后的数组内容和Kryo的源码,有兴趣的读者可以自行研究下。文中有陈述不完善的地方,也请批评指正,共同提高,多谢。

原创粉丝点击