spark 序列化 序列化方法和属性 Kryo序列化框架

什么时候需要序列化?

在实际开发中我们往往需要自己定义一些对于RDD的操作,那么需要注意的是,初始化工作是在Driver端进行的,而程序实际运行是在Executor端进行的,2者有可能不在同一节点,那么就涉及跨进程通信了,所以要进行序列化

进一步来说, 在Spark中,算子相关的操作在Excutor上执行,算子之外的代码在Driver端执行,在执行有些算子的时候,需要使用到Driver里面定义的数据,这就涉及到了跨节点或者跨进程之间的通信,所以要求传递给Excutor的数据所属的类型必须实现Serializable接口

如何判读是否实现了Serializable?

在作业job提交之前,其中有一行代码, val cleanF = sc.clean(f), 用于进行闭包检测,之所以叫闭包检测,是因为当前函数的内部访问了外部函数的变量,属于闭包的形式

错误演示

算子相关的操作在Excutor上执行,算子之外的代码在Driver端执行

package com.xcu.bigdata.spark.core.pg02_serializable

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}


object Spark01_Serializable {
  def main(args: Array[String]): Unit = {
    //创建配置文件
    val conf: SparkConf = new SparkConf().setAppName("Spark01_Serializable").setMaster("local[*]")
    //创建SparkContext,该对象是提交的入口
    val sc = new SparkContext(conf)
    //创建2个user对象
    val user1 = new User
    user1.name = "zhangsan"
    val user2 = new User
    user2.name = "lisi"
    //将两个user对象封装成一个List
    val rdd: RDD[User] = sc.parallelize(List(user1, user2))
    //打印user.name属于foreach算子相关的操作,在Excutor上执行
    //像创建new SparkContext、new User等属于算子以外的代码,在Driver端执行
    //如果不序列化,会出错
    rdd.foreach((user: User) => println(user.name))
    //释放资源
    sc.stop()
  }
}


class User {
  // 给name属性赋默认值
  var name: String = _
}

异常:

java.io.NotSerializableException

解决办法:

class User extends Serializable {
  // 给name属性赋默认值
  var name: String = _
}

序列化方法和属性

package com.xcu.bigdata.spark.core.pg02_serializable

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Desc : 序列化方法和属性
 */
object Spark02_Serializable {
  def main(args: Array[String]): Unit = {
    //创建配置文件
    val conf: SparkConf = new SparkConf().setAppName("Spark02_Serializable").setMaster("local[*]")
    //创建SparkContext,该对象是提交的入口
    val sc = new SparkContext(conf)
    //创建RDD
    val rdd: RDD[String] = sc.makeRDD(Array("hello world", "hello spark", "hive", "scala"))
    //创建一个Search对象
    val search = new Search("hello")
    //Driver:算子以外的代码都是在Driver端执行
    //Executor:算子里面的代码都是在Executor端执行
    //函数传递
    search.getMatch1(rdd).collect()
    //属性传递
    search.getMatch2(rdd).collect().foreach(println)
    //释放资源
    sc.stop()
  }
}

class Search(query: String) {
  def isMatch(s: String): Boolean = {
    s.contains(query)
  }

  //函数序列化案例
  def getMatch1(rdd: RDD[String]): RDD[String] = {
    //rdd.filter(this.isMatch)
    rdd.filter(isMatch)
  }

  //属性序列化案例
  def getMatch2(rdd: RDD[String]): RDD[String] = {
    //rdd.filter(x => x.contains(this.query))
    rdd.filter((x: String) => x.contains(query))
    //val q =query
    //rdd.filter(x => x.contains(q))
  }
}
问题一说明
    //过滤出包含字符串的RDD
    def getMatch1 (rdd: RDD[String]): RDD[String] = {
        rdd.filter(isMatch)
    }1)在这个方法中所调用的方法isMatch()是定义在Search这个类中的,实际上调用的是this. isMatch()this表示Search这个类的对象,程序在运行过程中需要将Search对象序列化以后传递到Executor端。
	(2)解决方案
		类继承scala.Serializable即可。
		class Search() extends Serializable{...}
问题二说明
    //过滤出包含字符串的RDD
    def getMatche2(rdd: RDD[String]): RDD[String] = {
        rdd.filter(x => x.contains(query))
    }1)在这个方法中所调用的方法query是定义在Search这个类中的字段,实际上调用的是this. query,this表示Search这个类的对象,程序在运行过程中需要将Search对象序列化以后传递到Executor端。
	(2)解决方案一
		(a)类继承scala.Serializable即可。
		class Search() extends Serializable{...}
		(b)将类变量query赋值给局部变量
            //过滤出包含字符串的RDD
            def getMatche2(rdd: RDD[String]): RDD[String] = {
                val q = this.query//将类变量赋值给局部变量
                rdd.filter(x => x.contains(q))
            }
解决方案二
	把Search类变成样例类,样例类默认是序列化的。
	case class Search(query:String) {...}

Kryo序列化框架

Java的序列化能够序列化任何的类。但是比较重,序列化后对象的提交也比较大.

Spark出于性能的考虑,Spark2.0开始支持另外一种Kryo序列化机制。Kryo速度是Serializable的10倍。当RDD在Shuffle数据的时候,简单数据类型、数组和字符串类型已经在Spark内部使用kryo来序列化

注意:即使使用kryo序列化,也要继承Serializable接口

package com.xcu.bigdata.spark.core.pg02_serializable

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @Desc : kryo的简单使用
 */

object Spark03_TestKryo {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf()
      .setAppName("Spark03_TestKryo")
      .setMaster("local[*]")
      // 替换默认的序列化机制
      .set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
      // 注册需要使用 kryo 序列化的自定义类
      .registerKryoClasses(Array(classOf[Searcher]))
    //创建SparkContext,该对象是提交的入口
    val sc = new SparkContext(conf)
    //创建RDD
    val rdd: RDD[String] = sc.makeRDD(Array("hello world", "hello scala", "spark", "java"), numSlices = 2)
    //创建一个Search对象
    val searcher = new Searcher("hello")
    //过滤特定字符串
    val result: RDD[String] = searcher.getMatchedRDD1(rdd)
    //打印输出
    result.collect.foreach(println)
  }
}

//样例类默认是序列化的
case class Searcher(val query: String) {

  def isMatch(s: String): Boolean = {
    s.contains(query)
  }

  def getMatchedRDD1(rdd: RDD[String]): RDD[String] = {
    rdd.filter(isMatch)
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值