spark(六)序列化及多線程問題

一、序列化

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章