Spark 累加器、广播变量

本文详细介绍了Apache Spark中的两种共享变量——累加器和广播变量。累加器用于分布式计算中的计数、求和等操作,如示例所示,通过实例化、注册、添加数据和获取值来使用。自定义累加器则通过继承AccumulatorV2实现。广播变量则能高效分发大对象到各节点,减少数据传输,例如在处理大量数据时结合黑名单进行过滤。使用广播变量需创建并广播对象,然后在任务中读取其值,确保只读操作。
摘要由CSDN通过智能技术生成

1. 累加器

Apache Spark 使用共享变量。当驱动程序向集群执行器发送任务时,集群的每个节点都会收到一份共享变量的副本。如果我们想实现向 MapReduce 上的计数器,显然是不可以的;如果我们想要更新这些副本的值,也无法影响驱动器的对中应变量。Apache Spark 支持两种基本类型的共享变量——累加器和广播。

当我们想要对数据进行关联操作时,可以使用累加器。累加器通过关联和交互操作,可实现计数、求和或求平均的功能。

累加器有两个实现类:

  • LongAccumulator :用于计算64位整数的总和、计数和平均值的累加器。
  • DoubleAccumulator :用于计算双精度浮点数的和、计数和平均数。

1.1 使用累加器

累加器实现步骤:

  1. 实例化累加器对象
  2. 使用 SparkContext 注册累加器对象
  3. 使用累加器进行数据的添加
  4. 使用 value() 获取累加器的数值
def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setMaster("local[2]").setAppName("Scala_Accumulator")
    val sc = new SparkContext(conf)

    val rdd = sc.makeRDD(1 to 10)

    // 1. 实例化累加器对象
    var acc = new LongAccumulator()

    // 2. 使用 `SparkContext` 注册累加器对象
    sc.register(acc)

    // 3. 使用累加器进行数据的添加
    rdd.map(x => {
        acc.add(1)
    }).collect().toList

    // 4. 使用 `value()` 获取累加器的数值
    println(acc.value)

}

1.2 自定义累加器

自定义累加器的功能提供在在 1.x 版本之后,但是在 2.0 版本之后,累加器的易用性有了较大的改进,并提供了 AccumulatorV2 抽象类。所以自定义累加器,可继承该类,并实现其中的方法。

class My_Accumulator extends AccumulatorV2[Int, Long] {
    // 创建成员属性用于记录当前累加器的值
    var count: Long = 0L

    /**
   * 用于判断当前累加器是否为初始状态
   *
   * @return
   */
    override def isZero: Boolean = this.count == 0

    /**
   * 复制当前累加器的状态
   */
    override def copy(): AccumulatorV2[Int, Long] = {
        val acc = new My_Accumulator
        acc.count = this.count
        acc
    }

    /**
   * 重置当前累加器的值
   */
    override def reset(): Unit = this.count = 0

    /**
   * 将传入的对象添加到当前的累加器值中
   *
   * @param v 累加的参数
   */
    override def add(v: Int): Unit = this.count += v

    /**
   * 将其他分区的累加器传入merge 并将所有累加器的值进行合并
   *
   * @param other 其他分区的累加器
   */
    override def merge(other: AccumulatorV2[Int, Long]): Unit = {
        val o = other.asInstanceOf[My_Accumulator]
        this.count += o.count
    }

    /**
   * @return 返回当前累加器的值
   */
    override def value: Long = this.count

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

    val conf = new SparkConf().setMaster("local[2]").setAppName("Scala_Accumulator")
    val sc = new SparkContext(conf)

    val rdd = sc.makeRDD(1 to 10)

    // 1. 实例化自定义的累加器对象
    var acc = new My_Accumulator

    // 2. 使用 `SparkContext` 注册自定义累加器对象
    sc.register(acc)

    // 3. 使用累加器进行数据的添加
    rdd.map(x => {
        acc.add(1)
    }).collect().toList

    // 4. 使用 `value()` 获取累加器的数值
    println(acc.value)

}

2. 广播变量

广播变量(Broadcast)允许 Spark 的不同节点上保存一个安全的只读缓存变量,通过广播变量可高效分发较大的对象。

使用广播变量后,使得每个节点的 executor 中的 cache 才存储副本,就不同为每个 task 创建副本了。

2.1 使用广播变量

使用广播变量:

  • 对一个类型为 T 的对象调用 SparkContext.broadcast 创建出一个 Broadcast[T] 对象
  • 通过 value 属性访问该对象的值
  • 变量只会被发送到各个节点一次,为避免副本被更改,应当作为只读处理
def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setMaster("local[2]").setAppName("Demo14")
    val sc = new SparkContext(conf)


    // 加载黑名单文件放入集合
    val source = Source.fromFile("C:\\Users\\Amos\\Desktop\\blackList.txt")
    //   文件大小1GB
    val blkList: List[String] = source.getLines().toList
    source.close()

    // 1. 创建广播对象
    val bc_blkList: Broadcast[List[String]] = sc.broadcast(blkList)

    // 加载日志数据创建RDD
    val rdd = sc.textFile("C:\\Users\\Amos\\Desktop\\weblog\\access.log-20211107")
    // 将日志数据通过处理得到  (ip,是否为黑名单用户)
    rdd
    .repartition(10)
    .map(line => {
        val ip = line.split(" ").head
        //2. 需要使用时  从公共缓存中读取对象
        val list = bc_blkList.value
        (ip, if (list.contains(ip)) 1 else 0)
    })
    .foreach(println)

}

 


❤️ END ❤️
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JOEL-T99

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值