• 26、高性能序列化类库


    一、数据序列化概述

    在任何分布式系统中,序列化都是扮演着一个重要的角色的。如果使用的序列化技术,在执行序列化操作的时候很慢,或者是序列化后的数据还是很大,
    那么会让分布式应用程序的性能下降很多。所以,进行Spark性能优化的第一步,就是进行序列化的性能优化。
    
    Spark自身默认就会在一些地方对数据进行序列化,比如Shuffle。还有就是,如果我们的算子函数使用到了外部的数据(比如Java内置类型,或者自定义类型),
    那么也需要让其可序列化。
    
    而Spark自身对于序列化的便捷性和性能进行了一个取舍和权衡。默认,Spark倾向于序列化的便捷性,使用了Java自身提供的序列化机制——基于ObjectInputStream和
    ObjectOutputStream的序列化机制。因为这种方式是Java原生提供的,很方便使用。
    
    但是问题是,Java序列化机制的性能并不高。序列化的速度相对较慢,而且序列化以后的数据,还是相对来说比较大,还是比较占用内存空间。因此,如果
    你的Spark应用程序对内存很敏感,那么,实际上默认的Java序列化机制并不是最好的选择。


    二、Spark提供的两种序列化机制

    Spark实际上提供了两种序列化机制,它只是默认使用了第一种:
    
    1、Java序列化机制:默认情况下,Spark使用Java自身的ObjectInputStream和ObjectOutputStream机制进行对象的序列化。
    只要你的类实现了Serializable接口,那么都是可以序列化的。而且Java序列化机制是提供了自定义序列化支持的,只要你
    实现Externalizable接口即可实现自己的更高性能的序列化算法。Java序列化机制的速度比较慢,而且序列化后的数据占用的内存空间比较大。
    
    2、Kryo序列化机制:Spark也支持使用Kryo类库来进行序列化。Kryo序列化机制比Java序列化机制更快,而且序列化后的数据占用的空间更小,
    通常比Java序列化的数据占用的空间要小10倍。Kryo序列化机制之所以不是默认序列化机制的原因是,有些类型虽然实现了Seriralizable接口,
    但是它也不一定能够进行序列化;此外,如果你要得到最佳的性能,Kryo还要求你在Spark应用程序中,对所有你需要序列化的类型都进行注册。


    三、如何使用Kryo序列化机制

    1、使用

    如果要使用Kryo序列化机制,首先要用SparkConf设置一个参数,使用new SparkConf().set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")即可,即将Spark的
    序列化器设置为KryoSerializer。这样,Spark在内部的一些操作,比如Shuffle,进行序列化时,就会使用Kryo类库进行高性能、快速、更低内存占用量的序列化了。
    
    使用Kryo时,它要求是需要序列化的类,是要预先进行注册的,以获得最佳性能——如果不注册的话,那么Kryo必须时刻保存类型的全限定名,反而占用不少内存。
    Spark默认是对Scala中常用的类型自动注册了Kryo的,都在AllScalaRegistry类中。
    
    但是,比如自己的算子中,使用了外部的自定义类型的对象,那么还是需要将其进行注册。
    
    (实际上,下面的写法是错误的,因为counter不是共享的,所以累加的功能是无法实现的)
    val counter = new Counter();
    val numbers = sc.parallelize(Array(1, 2, 3, 4, 5))
    numbers.foreach(num => counter.add(num));
    
    
    
    
    
    
    如果要注册自定义的类型,那么就使用如下的代码,即可:
    
    Scala版本:
    val conf = new SparkConf().setMaster(...).setAppName(...)
    conf.registerKryoClasses(Array(classOf[Counter] ))
    val sc = new SparkContext(conf)
    
    Java版本:
    SparkConf conf = new SparkConf().setMaster(...).setAppName(...)
    conf.registerKryoClasses(Counter.class)
    JavaSparkContext sc = new JavaSparkContext(conf)


    2、优化Kryo类库

    1、优化缓存大小
    如果注册的要序列化的自定义的类型,本身特别大,比如包含了超过100个field。那么就会导致要序列化的对象过大。此时就需要对Kryo本身进行优化。
    因为Kryo内部的缓存可能不够存放那么大的class对象。此时就需要调用SparkConf.set()方法,设置spark.kryoserializer.buffer.mb参数的值,将其调大。
    
    默认情况下它的值是2,就是说最大能缓存2M的对象,然后进行序列化。可以在必要时将其调大。比如设置为10。
    
    2、预先注册自定义类型
    虽然不注册自定义类型,Kryo类库也能正常工作,但是那样的话,对于它要序列化的每个对象,都会保存一份它的全限定类名。此时反而会耗费大量内存。
    因此通常都建议预先注册号要序列化的自定义的类。
    
    
    
    
    
    
    在什么场景下使用Kryo序列化类库?
    
    首先,这里讨论的都是Spark的一些普通的场景,一些特殊的场景,比如RDD的持久化,在后面会讲解。这里先不说。
    
    那么,这里针对的Kryo序列化类库的使用场景,就是算子函数使用到了外部的大数据的情况。比如说吧,我们在外部定义了一个封装了应用所有配置的对象,
    比如自定义了一个MyConfiguration对象,里面包含了100m的数据。然后,在算子函数里面,使用到了这个外部的大对象。
    
    此时呢,如果默认情况下,让Spark用java序列化机制来序列化这种外部的大对象,那么就会导致,序列化速度缓慢,并且序列化以后的数据还是比较大,比较占用内存空间。
    
    因此,在这种情况下,比较适合,切换到Kryo序列化类库,来对外部的大对象进行序列化操作。一是,序列化速度会变快;二是,会减少序列化后的数据占用的内存空间。
  • 相关阅读:
    博客地址
    Version 1.4.2_03 of the JVM not suitable for this product.解决
    http请求(一) 工具
    Service 的两种启动方法和区别
    软件开发过程应该采用集中优势兵力各个击破
    架构感悟
    嵌套事务模版
    软件行业对人才的依赖
    使用SQL Server 2005 新的语法ROW_NUMBER()进行分页的两种不同方式的性能比较
    架构设计中的分层与分区
  • 原文地址:https://www.cnblogs.com/weiyiming007/p/11262255.html
Copyright © 2020-2023  润新知