Spark2.X之累加器


前言

通常在向 Spark 传递函数时,比如使用 map() 函数或者用 filter() 传条件时, 可以使用驱动器程序中定义的变量,但是集群中运行的每个任务都会得到这些变 量的一份新的副本,更新这些副本的值也不会影响驱动器中的对应变量。这时使用累加器就可以实现我们想要的效果。


提示:以下是本篇文章正文内容,下面案例可供参考

一、累加器的作用

累加器支持在所有不同节点之间进行累加计算(比如计数、求和、集合累积)。

二、使用步骤

1.引入类

代码如下(示例):

import org.slf4j.{Logger, LoggerFactory}
import org.apache.spark.SparkConf
import java.util.Properties
import org.apache.spark.sql.SparkSession
import org.apache.spark.rdd.RDD
import org.apache.spark.SparkContext
import scala.collection.JavaConverters._

2.代码解析

代码如下(示例):

//scala集合的累加
var counter1: Int = 0
var data = Seq(1,2,3)
data.foreach(x => counter1 += x )
LOGGER.info("scala集合的累加")
println(counter1)//6
println("+++++++++++++++++++++++++")

//RDD的累加
var counter2: Int = 0
val dataRDD: RDD[Int] = sc.parallelize(data) //分布式集合的[1,2,3]
dataRDD.foreach(x => counter2 += x)
LOGGER.info("RDD的累加")
println(counter2)//0
println("+++++++++++++++++++++++++")

注意:上面的 RDD 操作中counter2运行结果是 0,因为 foreach 中的函数是传递给 Worker 中的 Executor 执行,用到了 counter2 变量,而 counter2 变量在 Driver 端定义的,在传递给 Executor 的时候,各个 Executor 都有了一份 counter2,最后各个 Executor 将各自个 x 加到自己的 counter2 上面了,和 Driver 端的 counter2 没有关系,如何解决?—使用累加器。

代码如下(示例):

//累加器longAccumulator的累加
val counter3 = sc.longAccumulator("count")
dataRDD.foreach(x => counter3.add(x))
LOGGER.info("累加器longAccumulator的累加")
println(counter3.value)//6
println("+++++++++++++++++++++++++")

有些场景会用到集合累加。
代码如下(示例):

//累加器longAccumulator的累加
//累加器collectionAccumulator的累加
val aList = List[String]("1")
val counter4 = sc.collectionAccumulator[List[String]]("add_list")
dataRDD.foreach(x =>{
  counter4.add(aList)
})
val accumulatorList = counter4.value.asScala.toList.flatten
LOGGER.info("累加器longAccumulator的累加")
println("accumulatorList:   "+accumulatorList)//List(1, 1, 1)

完整代码如下(示例):

import org.slf4j.{Logger, LoggerFactory}
import org.apache.spark.sql.SparkSession
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.JavaConverters._

object spark_accumulators_job {

  val LOGGER: Logger = LoggerFactory.getLogger(getClass)
  def main(args: Array[String]): Unit = {
    val sparkSession: SparkSession = SparkSession.builder().config(initConf()).enableHiveSupport().getOrCreate()
    val sc: SparkContext = sparkSession.sparkContext

    不使用累加器
    //var counter = 0
    //val data = Seq(1, 2, 3)
    //data.foreach(x => counter += x)
    //println("Counter value: "+ counter)

    将data转换成RDD,再来重新计算
    //var counter = 0
    //val data = Seq(1, 2, 3)
    //var rdd = sc.parallelize(data)
    //rdd.foreach(x => counter += x)
    //println("Counter value: "+ counter)
    
    //scala集合的累加
    var counter1: Int = 0
    var data = Seq(1,2,3)
    data.foreach(x => counter1 += x )
    LOGGER.info("scala集合的累加")
    println(counter1)//6
    println("+++++++++++++++++++++++++")
    //RDD的累加
    var counter2: Int = 0
    val dataRDD: RDD[Int] = sc.parallelize(data) //分布式集合的[1,2,3]
     dataRDD.foreach(x => counter2 += x)
    LOGGER.info("RDD的累加")
    println(counter2)//0
    println("+++++++++++++++++++++++++")
    //累加器longAccumulator的累加
    val counter3 = sc.longAccumulator("count")
    dataRDD.foreach(x => counter3.add(x))
    LOGGER.info("累加器longAccumulator的累加")
    println(counter3.value)//6
    println("+++++++++++++++++++++++++")

    //累加器collectionAccumulator的累加
    val aList = List[String]("1")
    val counter4 = sc.collectionAccumulator[List[String]]("add_list")
    dataRDD.foreach(x =>{
      counter4.add(aList)
    })
    val accumulatorList = counter4.value.asScala.toList.flatten
    LOGGER.info("累加器longAccumulator的累加")
    println("accumulatorList:   "+accumulatorList)//List(1, 1, 1)
  }

  def initConf(): SparkConf = {
    new SparkConf()
      .setAppName("Job")
      .registerKryoClasses(Array(
      classOf[org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema],
      classOf[org.apache.spark.sql.types.StructType],
      classOf[org.apache.spark.sql.types.StructField],
      classOf[org.apache.spark.sql.types.DataType],
      classOf[Array[org.apache.spark.sql.Row]],
      classOf[Array[org.apache.spark.sql.types.StructField]]
    ))
  }

}

总结

在默认情况下,当 Spark 在集群的多个不同节点的多个任务上并行运行一个函数时,它会把函数中涉及到的每个变量,在每个任务上都生成一个副本。累加器解决了需要在多个任务之间共享变量,或者在任务(Task)和任务控制节点 (Driver Program)之间共享变量的问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值