一、序列化对比
在分布式系统中扮演着重要的角色,优化Spark程序时,首当其冲的就是对序列化方式的优化。Spark为使用者提供两种序列化方式:
- Java Serialization: 默认的序列化方式。Spark内部是使用Java的序列化机制,ObjectOutputStream / ObjectInputStream,对象输入输出流机制,来进行序列化
- Kryo Serialization: 相较于 Java Serialization 的方式,速度更快,空间占用更小,但并不支持所有的序列化格式,同时使用的时候需要注册class。spark-sql中默认使用的是kyro的序列化方式。
二、Kryo序列化机制,生效的场景
2.1、算子函数中使用到的外部变量
优化网络传输的性能,可以优化集群中内存的占用和消耗
2.2、持久化RDD时进行序列化,StorageLevel.MEMORY_ONLY_SER
持久化RDD,优化内存的占用和消耗;持久化RDD占用的内存越少,task执行的时候,创建的对象,就不至于频繁的占满内存,频繁发生GC。
2.3、shuffle
在进行stage间的task的shuffle操作时,节点与节点之间的task会互相大量通过网络拉取和传输文件,此时,这些数据既然通过网络传输,也是可能要序列化的,就会使用Kryo
三、Kyro的使用
3.1、配置
有两种配置方式
- 在配置文件中,可以在
spark-default.conf
设置全局参数 - 在代码中设置
sparkConf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
3.2、注册, 也是就是为什么spark没有将kyro作为默认的序列化机制
sparkConf.registerKryoClasses(new Class[] {自定义的序列化的类});
3.2.1、如果你要序列化的对象比较大,可以增加参数spark.kryoserializer.buffer所设置的值。
3.2.2、弊端: 如果你没有注册需要序列化的class,Kyro依然可以照常工作,但会存储每个对象的全类名(full class name),这样的使用方式往往比默认的 Java serialization 还要浪费更多的空间。
3.2.3、可以设置 spark.kryo.registrationRequired 参数为 true,使用kyro时如果在应用中有类没有进行注册则会报错:
四、spark使用KryoRegistrator java代码示例
4.1、自定义序列化类
class Qualify implements Serializable
4.2、注册序列化类
import org.apache.spark.serializer.KryoRegistrator;
import com.esotericsoftware.kryo.Kryo;
public class MyRegistrator implements KryoRegistrator{
public void registerClasses(Kryo arg0) {
//注册序列化类
arg0.register(Qualify.class);
}
}
4.3、配置
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer");
conf.set("spark.kryo.registrator", "com.chb.test.kyro.MyRegistrator");
五、问题
5.1、object not serializable (class: org.apache.hadoop.io.BytesWritable)
spark操作返回的数据类型为BytesWriteable, 我有对返回数据进行其他操作(如, join),但是BytesWritable没有实现 java.io.Serializable
接口 , 所以运行过程中就会报错没有学历恶化
5.2、解决方式
5.2.1、指定如何序列化BytesWritable
手动指定
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
conf.registerKryoClasses(Array(classOf[org.apache.hadoop.io.BytesWritable]))
那么就是Kyro来序列化BytesWritable对象, 就可以在网络间传输了。
5.2.2、将BytesWritable转换成其他可序列化的对象
这种方法就是从BytesWritable 对象中抽取我们需要的数据,然后将它存储在其他可序列化的对象中,
如: 从BytesWritable 对象中抽取我们要的数据并转换成String,而 String 是实现 java.io.Serializable 接口的类,所以也可以解决上面的问题。