一、序列化
我們這裏不討論什麼是序列化以及序列化有什麼作用、序列化方式等問題。此處我們只討論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’程序時尤其要注意,因爲本地可能無法復現問題。
解決方案:使用線程安全的類代替、加鎖等