一、序列化
我们这里不讨论什么是序列化以及序列化有什么作用、序列化方式等问题。此处我们只讨论spark程序开发中序列化的一些问题
我们都知道spark程序实际计算时是在Executor上执行,因此Driver端的对象如果Executor要使用的话需要通过网络传输,因此对象一定要实现序列化接口,否则单机测试可能没问题,但是放到集群上就报序列化错误。
无论是直接使用,或通过广播发送,对象都要序列化。
二、序列化注意事项
先简要说一下scala中class object caseclass的区别
- class与java中的class类似
- object相当于单例类并且默认实现了序列化接口,其属性和方法都是静态的
- case class 不用new对象,默认实现了equals、hashCode方法,默认是可以序列化的,支持模式匹配
接下来我们看一下如下测试代码(本地测试)
object SerializableTest {
def main(args: Array[String]): Unit = {
//本地测试使用local模式,内存不够用需要设置testingMemory,线上不用
val conf: SparkConf = new SparkConf().setMaster("local[1]").setAppName("wordCount")
conf.set("spark.testing.memory", "536870912") //后面的值大于512m即可
val sc: SparkContext = new SparkContext(conf)
val rdd1: RDD[Int] = sc.makeRDD(1 to 20,4)
println("分区数:"+rdd1.getNumPartitions)
val rules = MyRules2
val resRdd = rdd1.mapPartitionsWithIndex((index,data) => {
var ruleMap = rules.rulesMap
val i: Int = ruleMap.getOrElse("hero", 0)
val id: Long = Thread.currentThread().getId
println(s"分区:$index,线程id $id,对象:$rules")
data.map((_, "分区号:" + index))
})
println(resRdd.collect().toBuffer)
sc.stop()
}
}
class MyRules extends Serializable {
val rulesMap: Map[String, Int] = Map("hero" -> 1, "king" -> 2)
}
object MyRules2 extends Serializable{
val rulesMap: Map[String, Int] = Map("hero" -> 1, "king" -> 2)
}
val rules = MyRules2分别使用new MyRules()和MyRules2
new MyRules()打印结果:
分区:0,线程id 45,对象:com.hk.scala.MyRules@8accbb
分区:1,线程id 45,对象:com.hk.scala.MyRules@182e2da
分区:2,线程id 45,对象:com.hk.scala.MyRules@1f6093
分区:3,线程id 45,对象:com.hk.scala.MyRules@1d43451
ArrayBuffer((1,分区号:0), (2,分区号:0), 。。。)
MyRules2打印结果:
分区:0,线程id 45,对象:com.hk.scala.MyRules2$@a246af
分区:1,线程id 45,对象:com.hk.scala.MyRules2$@a246af
分区:2,线程id 45,对象:com.hk.scala.MyRules2$@a246af
分区:3,线程id 45,对象:com.hk.scala.MyRules2$@a246af
ArrayBuffer((1,分区号:0), (2,分区号:0), 。。。)
class在Executor端每个Task都会生成一个对象,object方式每个Executor上的多个task共用一个对象。
当然是用object可以直接在map方法中使用,这样Driver端就不用实例化这个类了
三、多线程
这里说的多线程问题指的是,一个spark任务最重会有多个Executor执行,每个Executor相当于一个进程,会有多个线程执行任务。如果在Driver端初始化一个unsafe类的对象,并且该对象为为多个task线程共用则可能出现多线程问题,比如SimpleDataFormat类就是非线程安全的,这一点在开发spark’程序时尤其要注意,因为本地可能无法复现问题。
解决方案:使用线程安全的类代替、加锁等