Bootstrap

Spark性能调优:使用Kryo序列化库、RDD的压缩

使用Kryo序列化的原因

默认情况下,Spark内部是通过Java序列化(默认的序列化方式)形成一个二进制字节数组,大大减少了数据在内存、硬盘中占用的空间,减少了网络数据传输的开销,并且可以精确的推测内存使用情况,降低GC频率。

这种默认序列化机制的好处在于:处理起来比较方便,也不需要我们手动去做什么事情,只是在算子里面使用的变量必须是实现Serializable接口的,可序列化即可。
但是缺点在于:默认的序列化机制的效率不高,序列化的速度比较慢;序列化以后的数据,占用的内存空间相对还是比较大。

可以手动进行序列化格式的优化,Spark支持使用Kryo序列化机制。Kryo序列化机制比默认的Java序列化机制速度要快,序列化后的数据要更小,大概是Java序列化机制的1/10。所以Kryo序列化优化以后,可以让网络传输的数据变少,在集群中耗费的内存资源大大减少。


Kryo序列化生效位置

在Spark的架构中,在网络中传递的或者缓存在内存、硬盘中的对象需要进行序列化操作,序列化的作用主要是利用时间换空间:

1、算子函数中使用到的外部变量 ,会序列化,这时可以是用Kryo序列化机制;[广播变量]
2、持久化 RDD 时进行序列化,比如StorageLevel.MEMORY_ONLY_SER,可以使用 Kryo 进一步优化序列化的效率和性能;
3、进行shuffle时,比如在进行stage间的task的shuffle操作时,节点与节点之间的task会互相大量通过网络拉取和传输文件,此时,这些数据既然通过网络传输,也是可能要序列化的,就会使用Kryo。
4、分发给Executor上的Task。


默认对Task使用Java序列化

由于 Spark2.1.0默认对Task使用Java序列化(该序列化方式不允许修改,源码如下):


  /**
   * SparkEnv.scala
   * Helper method to create a SparkEnv for a driver or an executor.
   */
  private def create(
      conf: SparkConf,
      executorId: String,
      bindAddress: String,
      advertiseAddress: String,
      port: Int,
      isLocal: Boolean,
      numUsableCores: Int,
      ioEncryptionKey: Option[Array[Byte]],
      listenerBus: LiveListenerBus = null,
      mockOutputCommitCoordinator: Option[OutputCommitCoordinator] = None): SparkEnv = {

    val isDriver = executorId == SparkContext.DRIVER_IDENTIFIER

    ......

    val serializer = instantiateClassFromConf[Serializer](
      "spark.serializer", "org.apache.spark.serializer.JavaSerializer")
    logDebug(s"Using serializer: ${serializer.getClass}")

    ......

  }

Kryo序列化库主要参数介绍

先介绍几个相关的配置:

Property Name Default Meaning
spark.kryo.classesToRegister (none) 如果您使用Kryo序列化,请给出一个以逗号分隔的自定义类名称list列表,以向Kryo注册。有关更多细节,请参阅调优指南[tuning guide]。
spark.kryo.referenceTracking true 跟踪对同一个对象的引用情况,这对发现有循环引用或同一对象有多个副本的情况是很有用的。设置为f
;