1 RDD 中函数的传递
为什么要进行序列化操作?
因为类的对象是在driver端创建,而对象的方法是在executor上执行,一般情况它们不在同一个节点上,因此需要把driver端的对象序列化到executor端,否则程序会报错。
进行 Spark 进行编程的时候, 初始化工作是在 driver端完成的, 而实际的运行程序是在executor端进行的. 所以就涉及到了进程间的通讯, 数据是需要序列化的。
2 传递函数
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object SerDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("SerDemo")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(Array("hello world","hello atguigu","atguigu","hahah"),2)
//需求:在RDD中查出来包含query字符串的元素
//是在driver创建的对象 用来查找包含在hello子字符串的那些字符串组成的rdd
val searcher = new Searcher("hello")
val rdd2 = searcher.getMathchedRDD1(rdd1)
rdd2.collect().foreach(println)
sc.stop()
}
}
//query 为需要查找的子字符串
class Searcher(val query:String){
//判断s中是否包含子字符串query
def isMatch(s:String):Boolean ={
s.contains(query)
}
//判断出包含query字符串的字符串组成的新的RDD
def getMathchedRDD1(rdd:RDD[String])= {
//.filter是在driver调用
//isMatch方法在executor上执行
rdd.filter(isMatch)
}
}
直接运行程序会发现报错: 没有初始化。因为rdd.filter(isMatch) 用到了对象this的方法isMatch, 所以对象this需要序列化,才能把对象从driver发送到executor。
解决方案
3 序列化的方法
序列化的方法:
(1)java自带的序列化:只需要实现java的一个接口:Serializable
好处:及其简单,不需要做任何额外的工作;java自带,用起来方便。
坏处:序列化后的数据太重
序列化速度慢;序列化之后的size比较大;
(2)hadoop没有使用java的序列化
hadoop自定义序列化机制:…Writeable
注:spark中默认使用java自带的序列化
(3)支持另外一种序列化(第三方序列化方法)
Kyro序列化。
object SerDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[2]").setAppName("SerDemo")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(Array("hello world","hello atguigu","atguigu","hahah"),2)
//需求:在RDD中查出来包含query字符串的元素
//是在driver创建的对象 用来查找包含在hello子字符串的那些字符串组成的rdd
val searcher = new Searcher("hello")
val rdd2 = searcher.getMathchedRDD1(rdd1)
rdd2.collect().foreach(println)
sc.stop()
}
}
//query 为需要查找的子字符串
class Searcher(val query:String) extends Serializable {
//判断s中是否包含子字符串query
def isMatch(s:String):Boolean ={
s.contains(query)
}
//判断出包含query字符串的字符串组成的新的RDD
def getMathchedRDD1(rdd:RDD[String])= {
//.filter是在driver调用
//isMatch方法在executor上执行
rdd.filter(isMatch)
}
}
注意:若将getMathchedRDD方法改写成如下代码,同样也需要序列化
//若用到了对象的属性,所以类也需要序列化
def getMathchedRDD2(rdd:RDD[String]) ={
rdd.filter(x => x.contains(query))
}
方法的序列化:把方法所在的类实现接口:Serializable,这个类创建的对象就可以序列化。
属性的序列化:
1.把方法所在的类实现接口:Serializable,这个类创建的对象就可以序列化。
2.可以把属性的值存入到一个局部变量,然后传递局部变量(局部变量的类型需要为内置类型,如String、Int)。
def getMathchedRDD3(rdd:RDD[String]) ={
val q = query //将query赋值给局部变量q,q为内置的String类型,一般内置的变量类型都支持序列化,所以这种情况下不需要再进行序列化
rdd.filter(x => x.contains(q))
}
注意:有些值无法序列化:与外部存储系统的连接不能序列化。
4 序列化的原理
5 Kyro序列化操作
Java 的序列化比较重, 能够序列化任何的类.,比较灵活,但是相当的慢, 并且序列化后对象的体积也比较大。Spark 出于性能的考虑, 支持另外一种序列化机制: kryo (2.0开始支持),kryo 比较快和简洁.(速度是Serializable的10倍),想获取更好的性能应该使用 kryo 来序列化。
实现方法:
(1)指定序列化器:
.set("spark.serializer",classOf[Kryo].getName)
(2)注册需要使用Kyro序列化的类
.registerKryoClasses(Array(classOf[Searcher]))