Spark共享变量:广播变量、累加器

共享变量

当RDD中的转换算子需要用到定义在Driver中的变量的时候,计算节点在运行该转换算子之前,会通过网络将Driver中定义的变量下载到计算节点。同时如果计算节点在本地修改了下载的变量,该修改对Driver端定义的变量不可变(也就是计算节点的变量修改后并不会修改Driver端的变量)。

scala> sc.textFile("file:///root/data/word").foreach(line=> i=i+1)

scala> print(i)
0
广播变量

问题:
当出现超大数据集和小数据集进行连接的时候,能否使用join算子直接进行join,如果不行为什么?

//100GB
    var orderItems=List("001 apple 2 4.5","002 pear 1 2.0","001 瓜子 1 7.0")
    //10MB
    var users=List("001 zhangsan","002 lisi","003 wangwu")

    //日志富化操作 001 apple 2 4.5 -> 001 zhangsan apple 2 4.5
    var rdd1= sc.makeRDD(orderItems).map(line=>(line.split(" ")(0),line))
    var rdd2=sc.makeRDD(users).map(line=>(line.split(" ")(0),line))
    
    rdd1.join(rdd2).collect().foreach(println)

系统在做join的操作的时候会产生shuffle,会在各个计算节点当中传输100GB的数据用于完成join操作,因此join网络代价和内存代价都很高。

因此可以考虑将小数据定义成Driver中成员变量,在Map操作的时候完成join。

scala> var users=List("001 zhangsan","002 lisi","003 wangwu").map(line=>line.split(" ")).map(ts=>ts(0)->ts(1)).toMap
users: scala.collection.immutable.Map[String,String] = Map(001 -> zhangsan, 002 -> lisi, 003 -> wangwu)

scala> var orderItems=List("001 apple 2 4.5","002 pear 1 2.0","001 瓜子 1 7.0")
orderItems: List[String] = List(001 apple 2 4.5, 002 pear 1 2.0, 001 瓜子 1 7.0)

scala> import org.apache.spark.rdd.RDD
import org.apache.spark.rdd.RDD

scala> var rdd1= sc.makeRDD(orderItems).map(line=>(line.split(" ")(0),line))
rdd1: org.apache.spark.rdd.RDD[(String, String)] = MapPartitionsRDD[3] at map at <console>:27

scala> rdd1.collect
res2: Array[(String, String)] = Array((001,001 apple 2 4.5), (002,002 pear 1 2.0), (001,001 瓜子 1 7.0))

scala> rdd1.map(t=> t._2+"\t"+users.get(t._1).getOrElse("未知")).collect().foreach(println)
001 apple 2 4.5 zhangsan
002 pear 1 2.0  lisi
001 瓜子 1 7.0  zhangsan

但是上面的写法会存在一个问题,每当一个map算子遍历元素的时候都会向Driver下载userMap变量,虽然该值不大,但是在计算节点会频繁的下载。正是因为此种情景会导致没有必要的重复变量的copy,Spark提出了广播变量

Spark在程序运行前期,提前将需要广播的变量通知给所有的计算节点,计算节点会对需要广播的变量在计算之前进行下载操作,并且将该变量缓存(也就是一开始下载广播后,之后的操作直接读取自己的jvm内的变量,这里的下载次数是取决于分区数,也就是线程数,有几个线程下载几次)。该计算节点其他线程在使用到该变量的时候就不需要下载。

也就是没广播的话每个线程都得下载一次,有广播就整体下载一次就完事了

下载次数在广播之前取决于线程数,广播之后取决于进程数

scala> val ub = sc.broadcast(users)
ub: org.apache.spark.broadcast.Broadcast[scala.collection.immutable.Map[String,String]] = Broadcast(4)

scala> rdd1.map(t=>t._2+"\t"+ub.value.get(t._1).getOrElse("未知")).collect().foreach(println)
001 apple 2 4.5 zhangsan
002 pear 1 2.0  lisi
001 瓜子 1 7.0  zhangsan
计数器(累加器)

Spark提供的Accumulator,主要用于多个节点对一个变量进行共享性的操作。Accumulator只提供了累加的功能。但是确给我们提供了多个task对一个变量并行操作的功能。但是task只能对Accumulator进行累加操作,不能读取它的值。只有Driver程序可以读取Accumulator的值。

scala> val accum = sc.longAccumulator("My Accumulator")
accum: org.apache.spark.util.LongAccumulator = LongAccumulator(id: 0, name: Some(My Accumulator), value: 0)

scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum.add(x))
...
10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s

scala> accum.value
res2: Long = 10
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值