Spark系列八:序列化本质问题对Spark的影响

目录

方案一:直接在map创建规则实例

 方案二:闭包

方案三:静态对象

方案四:在Executor中实例化一个object 


需求:如何在spark中匹配规则,来删选单词。

方案一:直接在map创建规则实例

缺点:如果有许多数据那么处理一条就需要进入map,new 一个实例,它每个打印出来的toString地址都不一样,它用完这个实例就不在使用了,被垃圾回收了

在map的函数中,创建一个rules实例(太浪费资源)

object SerTest {

  def main(args: Array[String]): Unit = {    
    val conf = new SparkConf().setAppName("SerTest") 
    val sc = new SparkContext(conf)
    val lines: RDD[String] = sc.textFile(args(0))

    val r = lines.map(word => { //函数的执行是在Executor执行的(Task中执行的)  
      val rules = new Rules  //rules的实际是在Executor中使用的 
      val hostname = InetAddress.getLocalHost.getHostName //当前Task执行的主机名
      val threadName = Thread.currentThread().getName  //拿到当前线程的名字
      (hostname, threadName, Rules.rulesMap.getOrElse(word, 0), Rules.toString)
    })
    r.saveAsTextFile(args(1))
    sc.stop()
  }
}

class Rules  {
  val rulesMap = Map("hadoop" -> 1, "spark" -> 2)
}

 

 方案二:闭包

闭包:变量定义在外面,引用在函数里面

这个程序报错:Task not Serializable,原因是Task引用了一个没法被序列化的实例

 

object SerTest {

  def main(args: Array[String]): Unit = {    

    val rules = new Rules  //在Driver端被实例化,rules的实际是在Executor中使用的

    val conf = new SparkConf().setAppName("SerTest") 
    val sc = new SparkContext(conf)
    val lines: RDD[String] = sc.textFile(args(0))

    val r = lines.map(word => { //函数的执行是在Executor执行的(Task中执行的)  

      val hostname = InetAddress.getLocalHost.getHostName //当前Task执行的主机名
      val threadName = Thread.currentThread().getName  //拿到当前线程的名字
      (hostname, threadName, rules.rulesMap.getOrElse(word, 0), Rules.toString)
    })
    r.saveAsTextFile(args(1))
    sc.stop()
  }
}

class Rules  {
  val rulesMap = Map("hadoop" -> 1, "spark" -> 2)
}

 从图中可以看出Rules在Driver端生成,并放在每一个RDD里面,传送给Executor,这个传送石Rpc传送,所以需要把Rules给序列化了,

解决方案:

object SerTest {

  def main(args: Array[String]): Unit = {    

    val rules = new Rules  //在Driver端被实例化,rules的实际是在Executor中使用的
    println("@@@@@@@@@@@@" + rules.toString + "@@@@@@@@@@@@")
    val conf = new SparkConf().setAppName("SerTest") 
    val sc = new SparkContext(conf)
    val lines: RDD[String] = sc.textFile(args(0))

    val r = lines.map(word => { //函数的执行是在Executor执行的(Task中执行的)  

      val hostname = InetAddress.getLocalHost.getHostName //当前Task执行的主机名
      val threadName = Thread.currentThread().getName  //拿到当前线程的名字
      (hostname, threadName, rules.rulesMap.getOrElse(word, 0), Rules.toString)
    })
    r.saveAsTextFile(args(1))
    sc.stop()
  }
}

class Rules extends Serializable  {
  val rulesMap = Map("hadoop" -> 1, "spark" -> 2)
  val hostname = InetAddress.getLocalHost.getHostName
  println(hostname + "@@@@@@@") 
}

序列化后的Rules传送到Executor端,在反序列化,两边的Rules地址是不一样的,在map引用rules时,到Executor的task执行时,每个task里面的rules都是不一样的。

方案三:静态对象

目标:使每一个Executor里面都有一份单独的rules,而不是每一个Task里面都不相同

解决方案:广播,静态对象(单例对象,伴生对象)

object SerTest {

  def main(args: Array[String]): Unit = {    

    var rules = Rules   //初始化object(在Driver端)
    val conf = new SparkConf().setAppName("SerTest") 
    val sc = new SparkContext(conf)
    val lines: RDD[String] = sc.textFile(args(0))

    val r = lines.map(word => { //函数的执行是在Executor执行的(Task中执行的)  

      val hostname = InetAddress.getLocalHost.getHostName //当前Task执行的主机名
      val threadName = Thread.currentThread().getName  //拿到当前线程的名字
      (hostname, threadName, rules.rulesMap.getOrElse(word, 0), Rules.toString)
    })
    r.saveAsTextFile(args(1))
    sc.stop()
  }
}

object Rules extends Serializable  {
  val rulesMap = Map("hadoop" -> 1, "spark" -> 2)
}
 

效果:

方案四:在Executor中实例化一个object 

被使用时才会初始化,并且只会初始化一次,静态方法,类名.方法名

object SerTest {

  def main(args: Array[String]): Unit = {    

    val conf = new SparkConf().setAppName("SerTest") 
    val sc = new SparkContext(conf)
    val lines: RDD[String] = sc.textFile(args(0))

    val r = lines.map(word => { //函数的执行是在Executor执行的(Task中执行的)  

      val hostname = InetAddress.getLocalHost.getHostName //当前Task执行的主机名
      val threadName = Thread.currentThread().getName  //拿到当前线程的名字
      (hostname, threadName, Rules.rulesMap.getOrElse(word, 0), Rules.toString)
    })
    r.saveAsTextFile(args(1))
    sc.stop()
  }
}

object Rules { // 希望Rules在EXecutor中被初始化(不走网络了,就不必实现序列化接口)
  val rulesMap = Map("hadoop" -> 2.7, "spark" -> 2.2)
  val hostname = InetAddress.getLocalHost.getHostName
  println(hostname + "@@@@@@@@@@@@@@@@!!!!")
}

在一个Executor中多个Task会出现多线程问题,如果核数足够多,多个Task同时修改一个文件,就有可能出现线程不安全问题,解决方案是加锁,或者他的共享变量用线程安全的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值