Spark中的共享变量

import org.apache.log4j.{Level, Logger}
import org.apache.spark.{SparkConf, SparkContext}

/**
  * @author Jacky
  *         Spark中的共享变量
  *         在Spark中,只要不涉及任务的提交,那么所有的代码都在Spark程序的Driver端运行,
  *         也就是说,只要不触发Action算子,那么所有的代码都在Spark程序的Driver端运行,包括对RDD的定义和Transformation算子操作。
  *         总之,凡是不涉及到任务提交的代码片段,都是在Driver端运行。
  *
  *         在Spark中,当一个RDD的算子引用到了一个外部变量时,那么在执行Task任务的时候,每个task都会去Driver端
  *         拷贝一份外部变量的副本到Executor上运行。每个Task拷贝的副本只能是对应的Task去操作,其他的Task是不能操作
  *         另外一个Task拷贝的副本数据,即这个副本是不能共享的。
  *         由于外部变量不是共享的,那么当一个Job任务有很多个Task的时候,就意味着要拷贝的副本数就很多,比如,一个外部变量
  *         的数据大小是1G,此时这个Job一共有1000个Task任务需要执行,那么这1000个Task就要从Driver端拷贝1000G的数据(1TB)到
  *         Executor上执行。可以想像这会极大的消耗网络流量传输,同时也会消耗Executor大量的内存。
  *         这样就会大大降低Spark作业的运行效率。
  *
  *         在Spark的Executor上的数据都是由BlockManager来管理的。
  *         要解决上面的问题,此时我们可以将外部变量做成广播变量,那么每个Executor只会有一份外部变量的副本,
  *         在Executor上执行的Task都共享这份外部变量的数据。
  *
  *         第一步:Task先去自己所持有的BlockManager中查找是否存在这个外部变量。
  *         第二步:如果在自己的BlockManager中查找不到这个外部变量,此时它就会去近邻的Executor的BlockManager中查找这个外部变量。
  *         第三步:如果在所有的Executor的BlockManager中找不到这个外部变量,那么这个Task就会去Driver端拷贝一份外部变量的副本存储
  *         到Executor的BlockManager中。
  *
  *         由于每个Executor上只有一份外部变量的副本数据,那么此时需要拷贝的数据量大大减少,这样会直接降低网络流量传输,
  *         同时也会减少Executor内存消耗。
  */
object Scala_ShareVariableDemo {
  def main(args: Array[String]): Unit = {
    //在Driver端运行
    Logger.getLogger("org").setLevel(Level.WARN)
    //在Driver端运行
    val conf = new SparkConf().setAppName("Scala_ShareVariableDemo").setMaster("local")
    //在Driver端运行
    val sc = new SparkContext(conf)
    //在Driver端运行,在Driver端定义了一个num=2的这样的一个变量
    var num = 2
    //通过集合创建初始的RDD
    //这个也是在Driver端运行,这里只是定义了RDD1,并没有真正的执行,也即并没有真正的形成一个RDD,并指定分区数为2。
    val rdd1 = sc.parallelize(Array("hello", "word", "spark", "flink"), 2)
    //由于map算子也是Transformation类型的算子,它不触发任务的提交,那么这种操作也是在Driver端运行。
    //凡时对RDD进行Transformation算子转换操作,都是在Driver端运行的。
    val rdd2 = rdd1.map((_, 1))
    //注意这里就有点不同了,由于foreach它是一个Action算子,在Spark中一个RDD只要触发了一个Action算子,
    //那么就意味着触发了一个Job,此时Spark就会将这个Job划分成一个一个的Stage,然后形成一个一个的Task,
    //最后会将这些Task提交到Executor上运行。
    rdd2.foreach(x => {
      //定义在RDD算子里面的变量,称为内部变量
      val num2 = 7
      //定义在RDD算子外面的变量,称为外部变量
      num = num + x._2
      //在算子内部打印num
      //这里是在Executor上的Task中打印的,打印的是对num副本的操作后的结果。
      println(num)
    })
    //这个代码也是在Driver端运行
    println(num)

    sc.stop()
  }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值