版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012292754/article/details/86241359
1 数据序列化
- 在任何分布式的系统,序列化很重要,如果使用的序列化技术在执行序列化的时候很慢,或者序列化后数据还是很大,那么会导致分布式程序性能下降很多。所以 Spark 性能优化的第一步,就是进行序列化的性能优化;
- Spark 默认会在一些地方对数据进行序列化,比如
shuffle
。此外,如果用户的算子函数使用了外部的数据(比如 java 内置类型或者自定义类型),那么也需要对其序列化; - Spark 自身对于序列化的便捷性和性能进行了一个取舍。默认,Spark 倾向序列化的便捷性,使用了 Java 自身提供的序列化机制—— 基于
ObjectInputStream
和ObjectOutputStream
的序列化机制,因为这是 Java 原生提供的,方便使用。 - 但是 Java 序列化机制的性能不高,序列化速度较慢,而且序列化后的数据还是比较大,会占用较多的内存。
2 Spark 提供的两种序列化机制
- Java 序列化机制
默认情况下,Spark使用Java 自身的
ObjectInputStream
和OnjectOutputStream
机制进行对象的序列化。只要你的类实现了Serializable 接口
,那就可以序列化。而且 Java 序列化机制是提供了自定义序列化支持的,只要你实现Externalizable 接口
即可实现自己的更高性能的序列化算法。Java 序列化机智的速度比较慢,而且序列化后的数据占用的内存空间也比较大。
- Kryo 序列化机制
Kryo 序列化机制比 Java 序列化机制更快,而且序列化后数据占用的空间更小,通常比Java 序列化的数据占用的空间要小10倍。Kryo序列化机制之所以不是默认的,因为有些类型虽然实现了 Serializable 接口。但是它也不一定能够进行序列化,此外,如果你要得到最佳的性能,Kryo 还要求你在 Spark 应用程序中对所有你需要序列化的类型都进行注册。
3 如何使用 Kryo序列化
- 首先
SparkConf
设置一个参数,SparkConf.set("spark.serializer","org.apache.spark.serializer.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(counter.add(_))
3.1 注册自定义类型
3.1.1 Scala 版本
val conf= new SparkConf().setMaster().setAppName()
conf.registerKryoClasses(Array(classOf[Counter]))
val sc = new SparkContext(conf)
3.1.2 Java 版本
SparkConf conf = new SparkConf().setMaster().setAppName()
conf.registerKryoClasses(Counter.class)
JavaSparkContext sc = new JavaSparkContext(conf)
4 优化 Kryo类库
的使用
- 优化缓存的大小
- 如果注册的要序列化的自定义的类型本身特别大,比如包含了超过100个field,那么就会导致要序列化的对象太大。此时就需要对 Kryo 本身进行优化。因为 Kryo 内部的缓存可能不够存放那么大的 class 对象。此时就需要调用 SparkConf.set() 方法,设置
spark.kryoserializer.buffer.mb
参数的值,将其调大; - 默认它的值是2,就是说最大能缓存 2M 对象,然后进行序列化。可以在必要的时候将其调大,比如设置为 10;
- 预先注册自定义类型
- 虽然不注册自定义类型,Kryo 类库也可以正常工作,但是这样对于它要序列化的每个对象,都会保存一份它的全限定类名。此时反而会消耗大量内存。因此通常都建议预先注册好要序列化的自定义的类。
4 什么场景下使用 Kryo类库
- 算子函数使用了外部的大数据的情况,比如说,我们在外部定义了一个封装了应用所有配置的对象,比如自定义了一个 MyConf 对象,里面包含了100M 的数据,然后在算子函数里,使用了这个外部的大对象
- 此时,如果使用 Spark 默认的 Java 序列化机制来序列化这个外部的大对象,就会导致序列化速度缓慢,并且序列化以后的数据比较占用内存。因此,这种情况适合用
Kryo类库