RDD序列化
从计算的角度, 算子以外的代码都是在 Driver 端执行, 算子里面的代码都是在 Executor端执行。所以需要涉及到网络传输,且如果计算中涉及到算子以外的数据那么就要求这个数据进行序列化,因为没有序列化就代表无法进行网络传输也就无法将值传到其它Excutor端执行,就会发生错误。
(1)闭包检查
在scala的函数式编程中,函数内经常会用到函数外变量,这样就会形成闭包效果,因此,在分布式系统中,就会存在对象需要在Driver和Executor间传递,那么就需要传递的对象被序列化,因此在计算之前就会进行闭包检查,即检查使用到的对象是否进行了序列化。
(2)Kryo序列化框架
这个序列化框架不同于Java的序列化框架,相比于java的序列化框架,这个框架速度更快,生成的序列化文件更小,而且这个框架会忽略Java的一些序列化机制,例如Java中使用了transient关键字忽略对一些数据的序列化,而Kryo会忽略这个关键字。Spark 出于性能的考虑,Spark2.0 开始支持另外一种 Kryo 序列化机制。Kryo 速度是 Serializable 的 10 倍。当 RDD 在 Shuffle 数据的时候,简单数据类型、数组和字符串类型已经在 Spark 内部使用 Kryo 来序列化。
样例:
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[2]")
val sc: SparkContext = new SparkContext(conf)
val rdd: RDD[String] = sc.makeRDD(Array("hello world", "hello spark", "hive", "atguigu"))
val search = new Search("hello")
//函数传递,打印:ERROR Task not serializable
search.getMatch1(rdd).collect().foreach(println)
//若将query变为方法中的局部变量那么就不会报错
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] = {
val s = query
rdd.filter(x => x.contains(s))
}
}
之前发生错误是因为涉及到search中的属性query,query在scala语法中代表Search的构造参数,而sparkRDD在执行前需要进行闭包检测。对类的构造参数进行闭包检测其实就相当于对这个类进行闭包检测,而Search类是没有进行序列化的,所以就会报Task not serializable错。
RDD血缘关系
RDD 只支持粗粒度转换,即在大量记录上执行的单个操作。将创建 RDD 的一系列 Lineage(血统)记录下来,以便恢复丢失的分区。RDD 的 Lineage 会记录RDD 的元数据信息和转换行为,当该 RDD 的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区。
val rdd: RDD[String] = sc.makeRDD(List("hello world", "hello spark"))
println(rdd.toDebugString) // 打印出当前RDD的血缘关系
println("*************************")
val words: RDD[String] = rdd.flatMap((_: String).split(" "))
println(words.toDebugString)
println("*************************")
val wordToOne: RDD[(String, Int)] = words.map((word: String) =>(word,1))
println(wordToOne.toDebugString)
println("*************************")
val wordToSum: RDD[(String, Int)] = wordToOne.reduceByKey((_: Int)+(_: Int))
println(wordToSum.toDebugString)
println("*************************")
val array: Array[(String, Int)] = wordToSum.collect()
array.foreach(println)