sparkCore之共享变量

概述: 所谓共享变量,是为了解决task中使用到外部变量造成相关问题而出现的。spark提供了有限的两种共享变量:广播变量Broadcast变量和累加器Accumulator。

一、 Broadcast

1、使用说明

使用非常简单,只需要将普通的变量包装为Broadcast即可:
​ val xxBC:Broadcast[T] = sc.broadcast(t);
其中T是被包装的变量t的类型。

​ 在transformation中使用的时候,只需要xxBC.value即可以获取被包装的值。

2、使用注意点

广播变量不适合处理那些大变量,还有就是不适合处理那些频繁更新的值。

3、案例

🌰:
/*
使用 Broadcast方式高效的完成类似大小表关联的操作
*/
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkContext, SparkConf}

object BroadCast {
  def main(args: Array[String]) {
    val conf =new SparkConf()
      .setAppName("BroadCast")
      .setMaster("local[*]")
    val sc = new SparkContext(conf)
    //小表,学生表
    val stuMap = List(
      "1 zhangsan  22  bd-bj",
      "2 lisi  25  bd-bj",
      "3 zhangyaoshi  24  bd-sz",
      "4 hudada 18  bd-wh",
      "5 chenpingan  25  bd-bj"
    ).map(line => {
      val sid = line.substring(0,1)
      val info = line.substring(1).trim()
      (sid,info)
    }).toMap
    //创建广播变量
    val stuBC:Broadcast[Map[String,String]] =sc.broadcast(stuMap)
    //大表,成绩表
    val scores = List(
      "1 1 math 82",
      "2 1 english 0",
      "3 2 chinese 85.5",
      "4 3 PE 99",
      "5 5 math 99"
    )
    val scoreRDD:RDD[String] = sc.parallelize(scores)
    scoreRDD.map(scoreLine =>{
      val filds = scoreLine.split("\\s+")
      val sid = filds(1)
      //取出广播变量的值
      val bcMap = stuBC.value
      val info = bcMap.getOrElse(sid,null)
      s"${sid}\t${info}\t${filds(2)}\t${filds(3)}"
    }).foreach(println)
  }
}

二、Accumulator

1、使用说明

累加器的入口也是SparkContext,操作也比较简单,通过sc.accumulator(初始化的值),返回值就是一个累加器,累加器允许的操作就是++(add)。并且我们应该在driver端去获取对应的累加结果

2、使用注意点

①在指定累加器的时候可以根据需要指定一个累加器名称,方面我们可以在web-ui/4040页面查看具体信息,如果没有指定累加器名称,查看不到具体的累加信息。

②累加器的执行,必须要使用action去触发,也就是说累加器的操作必须要在transformation来累加。

③累加器值的调用,应该要在action之后。

④多次触发action操作,可能会造成累加器的多次执行,所以需要做到一旦调用完累加器做好累加器数据的重置。

3、案例

案例说明:
在将所有结果输出到文件的同时,额外的统计is和that出现了多少次,并将结果打印到控制台。

🌰:
import org.apache.spark.{SparkContext, SparkConf}


/*
在将所有结果输出到文件的同时,额外的统计is和that出现了多少次,并将结果打印到控制台。
 */
object Accumulator{
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName("AccumulatorOps")
      .setMaster("local[*]")
    val sc = new SparkContext(conf)
    val accu = sc.longAccumulator("isAccu")
    val list = List("is that this is as was from where")
    val lines =sc.parallelize(list)

    //统计is 和that出现多少次,然后将结果打印到控制台
    val pairs = lines.flatMap(_.split("\\s+"))
      .map(word => {
        if(word == "is" || word == "that") {
          accu.add(1)
        }
        (word, 1)
      })

    val ret = pairs.reduceByKey(_+_)

    println("触发之前的累加的结果:" + accu.value)
    ret.foreach(println)

    println("触发之后累加的结果:" + accu.value)

    accu.reset()
    ret.foreach(println)
    println("触发之后累加的结果2:" + accu.value)

    Thread.sleep(100000L)
    sc.stop()
  }
}

4、自定义累加器

🌰:
/*自定义累加器
    AccumulatorV2[IN, OUT]
        IN代表的是执行累加器add加入的指的类型
        OUT代表的是执行累加器value返回值的类型

    本例中IN使用String,其实代表的就是对应得要累加的字符串word
    返回值,不会是每一个word的次数k-v
 */
 import org.apache.spark.util.AccumulatorV2

import scala.collection.mutable


class MyAccumulator extends AccumulatorV2[String,scala.collection.mutable.Map[String,Int]]{
  var map = scala.collection.mutable.Map[String, Int]()
  //累加器是否有初始值
  override def isZero: Boolean = true

  //重置累加器
  override def reset(): Unit = map.clear()

  //获取累加器的值
  override def value: mutable.Map[String, Int] = map

  //拷贝其他task中对应累加器对应的值
  override def copy(): AccumulatorV2[String, mutable.Map[String, Int]] = {
    val mAccu = new MyAccumulator
    mAccu.map = this.map
    mAccu
  }


  //进行累加操作
  override def add(word: String): Unit = {
    //        val vOption = map.get(word)
    //        var count = 1
    /*if(vOption.isDefined) {//map中已经存在该word
        map.put(word, 1 + vOption.get)
    } else {//map中没有该word
        map.put(word, 1)
    }*/
    /*if(vOption.isDefined) {
        count += vOption.get
    }
    map.put(word, count)*/
    map.put(word,map.getOrElse(word,0)+1)
  }

  //合并累加器的值
  override def merge(other: AccumulatorV2[String, mutable.Map[String, Int]]): Unit = {
    val otherMap = other.value
    for((word,count) <- otherMap){
      map.put(word,map.getOrElse(word,0)+count)
    }
  }
}
/*
使用自定义累加器
*/
import org.apache.spark.{SparkContext, SparkConf}

object UseMyAcculator {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
      .setAppName(s"${UseMyAcculator.getClass.getSimpleName}")
      .setMaster("local[2]")
    val sc = new SparkContext(conf)

    val list = List(
      "hello you",
      "i hate you",
      "i miss you",
      "i love you",
      "get you"
    )
    val words = sc.parallelize(list).flatMap(_.split("\\s+"))
    //实例化自定义累加器并注册
    val myAccu = new MyAccumulator
    sc.register(myAccu, "sparkAccu")

    val pairs = words.map(word => {
      if (word == "you" || word == "hate") {
        myAccu.add(word)
      } else {
        (word, 1)
      }
    })
    pairs.count()
    println("you出现的次数:" + myAccu.value + "action执行之后")
    sc.stop()
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值