【Spark七十八】Spark Kyro序列化

本文介绍Spark中Kryo序列化机制的优势及使用方法。Kryo提供更快更紧凑的序列化方式,尤其在网络传输和磁盘溢出时能显著提高性能。文章详细展示了如何注册自定义类以支持Kryo序列化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当使用SparkContext的saveAsObjectFile方法将对象序列化到文件,以及通过objectFile方法将对象从文件反序列出来的时候,Spark默认使用Java的序列化以及反序列化机制,通常情况下,这种序列化机制是很低效的,Spark支持使用Kyro作为对象的序列化和反序列化机制,序列化的速度比java更快,但是使用Kyro时要注意,Kyro目前还是有些bug。

  • Spark默认是使用Java的ObjectOutputStream框架,它支持所有的继承于java.io.Serializable序列化,如果想要进行调优的话,可以通过继承java.io.Externalizable。这种格式比较大,而且速度慢。
  • Spark还支持这种方式Kryo serialization,它的速度快,而且压缩比高于Java的序列化,但是它不支持所有的Serializable格式,并且需要在程序里面注册。它需要在实例化SparkContext之前进行注册
When Spark is transferring data over the network or spilling data to disk, it needs to
serialize objects into a binary format. This comes into play during shuffle operations,
where potentially large amounts of data are transferred. By default Spark will use
Java’s built-in serializer. Spark also supports the use of Kryo, a third-party serialization
library that improves on Java’s serialization by offering both faster serialization
times and a more compact binary representation, but cannot serialize all types of
objects “out of the box.” Almost all applications will benefit from shifting to Kryo for
serialization.

 

 

代码示例:

package spark.examples.kryo

import com.esotericsoftware.kryo.Kryo
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.serializer.KryoRegistrator

//两个成员变量name和age,同时必须实现java.io.Serializable接口
class MyClass1(val name: String, val age: Int) extends java.io.Serializable {
}

//两个成员变量name和age,同时必须实现java.io.Serializable接口
class MyClass2(val name: String, val age: Int) extends java.io.Serializable {

}

//注册使用Kryo序列化的类,要求MyClass1和MyClass2必须实现java.io.Serializable
class MyKryoRegistrator extends KryoRegistrator {
  override def registerClasses(kryo: Kryo): Unit = {
    kryo.register(classOf[MyClass1]);
    kryo.register(classOf[MyClass2]);
  }
}

object SparkKryo {
  def main(args: Array[String]) {
    //设置序列化器为KryoSerializer,也可以在配置文件中进行配置
    System.setProperty("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
    System.setProperty("spark.kryo.registrator", "spark.examples.kryo.MyKryoRegistrator")
    val conf = new SparkConf()
    conf.setAppName("SparkKryo")
    conf.setMaster("local[3]")

    val sc = new SparkContext(conf)
    val rdd = sc.parallelize(List(new MyClass1("Tom", 31), new MyClass1("Jack", 23), new MyClass1("Mary", 19)))
    val fileDir = "file:///d:/wordcount" + System.currentTimeMillis()
    //将rdd中的对象通过kyro进行序列化,保存到fileDir目录中
    rdd.saveAsObjectFile(fileDir)
    //读取part-00000文件中的数据,对它进行反序列化,,得到对象集合rdd1
    val rdd1 = sc.objectFile[MyClass1](fileDir + "/" + "part-00000")


    rdd1.foreachPartition(iter => {
      while (iter.hasNext) {
        val objOfMyClass1 = iter.next();
        println(objOfMyClass1.name)
      }
    })

    sc.stop
  }
}

 

查看保存到文件中的内容,是个二进制数据:

SEQ!org.apache.hadoop.io.NullWritable"org.apache.hadoop.io.BytesWritable      蓑_xi??蛔?z汲   i       e ur [Lspark.examples.kryo.MyClass1;?
独#v?  xp   sr spark.examples.kryo.MyClass1z 峌#   xp

 

 

问题:

对于普通字符,数字,字符串写入到object文件,是否也是序列化的过程?明确指定使用kvro序列化Int之后,保存的文件确实是二进制的。去掉对Int的注册之后,结果还是一样,序列化的结果完全一样,结果都是:

SEQ!org.apache.hadoop.io.NullWritable"org.apache.hadoop.io.BytesWritable      F脗?庻籡陭姯&?  '       # ur [IM篳&v瓴?  xp         

 

 

 

import com.esotericsoftware.kryo.Kryo
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.serializer.KryoRegistrator

//注册使用Kryo序列化的类,对Int进行序列化
class MyKryoRegistrator extends KryoRegistrator {
  override def registerClasses(kryo: Kryo): Unit = {
    kryo.register(classOf[Int]);
  }
}

object SparkKryoPrimitiveType {
  def main(args: Array[String]) {
    //设置序列化器为KryoSerializer,也可以在配置文件中进行配置
    System.setProperty("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
    System.setProperty("spark.kryo.registrator", "spark.examples.kryo.MyKryoRegistrator")
    val conf = new SparkConf()
    conf.setAppName("SparkKryoPrimitiveType")
    conf.setMaster("local[3]")

    val sc = new SparkContext(conf)
    val rdd = sc.parallelize(List(1, 3, 7, 9, 11, 22))
    val fileDir = "file:///d:/wordcount" + System.currentTimeMillis()
    //将rdd中的对象通过kyro进行序列化,保存到fileDir目录中
    rdd.saveAsObjectFile(fileDir)
    //读取part-00000文件中的数据,对它进行反序列化,,得到对象集合rdd1
    val rdd1 = sc.objectFile[Int](fileDir + "/" + "part-00000")

    rdd1.foreachPartition(iter => {
      while (iter.hasNext) {
        println(iter.next())
      }
    })

    sc.stop
  }
}

 

 其它:

指定使用Kyro序列化,以及注册Kyro序列化类,可可以使用如下方式

val conf = new SparkConf()
//这句是多于的,调用conf的registerKryoClasses时,已经设置了序列化方法
conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
// Be strict about class registration
///如果一个要序列化的类没有进行Kryo注册,则强制Spark报错
conf.set("spark.kryo.registrationRequired", "true")
conf.registerKryoClasses(Array(classOf[MyClass], classOf[MyOtherClass]))

 

  /**
   * Use Kryo serialization and register the given set of classes with Kryo.
   * If called multiple times, this will append the classes from all calls together.
   */
  def registerKryoClasses(classes: Array[Class[_]]): SparkConf = {
    val allClassNames = new LinkedHashSet[String]()
    allClassNames ++= get("spark.kryo.classesToRegister", "").split(',').filter(!_.isEmpty)
    allClassNames ++= classes.map(_.getName)

    set("spark.kryo.classesToRegister", allClassNames.mkString(","))
    set("spark.serializer", classOf[KryoSerializer].getName)
    this
  }

 

 

<think>好的,我现在需要处理用户关于Apache Spark 3.2.4的问题。用户想要查找相关的使用指南、更新日志及常见问题解决方法。首先,我应该先回顾用户提供的引用内容,看看是否有可以直接利用的信息。 用户提供的三个引用中,引用[1]提到了配置Spark的history server来查看日志,这可能与日志调试相关,常见问题之一。引用[2]是下载Spark 2.4.4的示例,虽然版本不同,但可以引导用户到官方网站获取最新版本。引用[3]涉及设置JAVA_HOME,这可能与环境配置问题有关,常见于安装步骤中。 接下来,我需要整理Apache Spark 3.2.4的主要资源渠道。首先是官网文档,这是最权威的来源,应该建议用户查阅。然后是更新日志,官网通常会提供每个版本的详细Release Notes,列出新功能、改进和修复的问题。对于常见问题,可以结合引用中的配置问题,比如日志查看、环境变量设置,以及可能遇到的依赖冲突、资源分配问题等。 另外,用户可能需要知道如何下载和安装Spark 3.2.4,虽然引用[2]是旧版本的下载链接,但可以指导用户修改版本号到官网获取正确版本。同时,需要注意Spark版本与Hadoop版本的兼容性,这也是安装时的常见问题。 在回答结构上,按照用户的要求,需要分点清晰,包括获取资料、更新日志、常见问题及解决方法,并生成相关问题。需要确保所有数学公式和代码格式正确,但当前问题不涉及数学公式,主要是代码块和配置示例,比如修改配置文件或设置环境变量,这些需要用代码块格式。 可能还需要补充一些常见问题,比如在YARN模式下运行的问题,资源管理,或者性能调优,这些是Spark使用中的典型问题。结合引用[1]中的history server配置,可以作为一个常见问题点。 最后,生成的相关问题需要围绕用户可能进一步询问的方向,比如安装步骤、版本兼容性、性能优化、日志调试等,确保覆盖用户可能的需求。</think>### Apache Spark 3.2.4 使用指南与常见问题解答 #### 1. **获取官方资料** - **官方文档**:访问 [Apache Spark 官方文档](https://spark.apache.org/docs/3.2.4/),包含安装配置、编程指南(RDD/DataFrame/SQL/Streaming)及性能调优等内容[^官网]。 - **下载地址**:通过镜像站点下载安装包(替换版本号): ```bash wget https://mirrors.huaweicloud.com/apache/spark/spark-3.2.4/spark-3.2.4-bin-hadoop3.2.tgz ``` Hadoop 版本需与集群匹配[^2]。 #### 2. **更新日志与特性** - **Release Notes**:查看 [Spark 3.2.4 Release Notes](https://spark.apache.org/releases/spark-release-3-2-4.html),主要包含稳定性修复和依赖库升级(如 Hadoop 3.3.1、Kubernetes 支持改进)[^官网]。 #### 3. **常见问题与解决方法** - **环境配置问题** - **Java 版本不兼容**:需 JDK 8/11,设置 `JAVA_HOME`(参考引用[3]): ```bash export JAVA_HOME="/usr/lib/jvm/java-11-openjdk" ``` - **Hadoop 依赖缺失**:安装对应 Hadoop 版本并配置 `HADOOP_CONF_DIR`。 - **日志查看问题** 若 YARN 模式下无法查看日志,需启用 Spark History Server(参考引用[1]): ```properties # spark-defaults.conf spark.eventLog.enabled=true spark.eventLog.dir=hdfs:///spark-logs spark.history.fs.logDirectory=hdfs:///spark-logs ``` - **资源分配异常** 调整 `spark-submit` 参数: ```bash spark-submit --master yarn --num-executors 4 --executor-memory 4g ... ``` - **依赖冲突** 使用 `--packages` 指定库版本或构建包含依赖的 FAT JAR。 #### 4. **性能优化建议** - 启用动态资源分配:`spark.dynamicAllocation.enabled=true` - 使用 `Kyro` 序列化:`spark.serializer=org.apache.spark.serializer.KryoSerializer` - 调整 Shuffle 分区数:`spark.sql.shuffle.partitions=200`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值