从examples中学Spark(一):BroadcastTest.scala

之前学习都是官网API Doc+社区的博文,最近参考了几篇examples,发觉example+doc才是绝配。
由于集群Spark版本是2.1.1,所以我学习的examples示例是2.1.1版本中的,2.2.0版本中关于ml【也就是DataFrame版的mllib】的examples有不少内容与2.1.1版本不同
**注意:**使用ml的一些example还需要导入examples下的scopt_2.11-3.3.0.jar和spark-examples_2.11-2.1.1.jar

##学习前(理论)

  1. 默认情况下,函数中使用到的每一个变量会拷贝到每一个执行函数的节点(Worker)的【在Spark On Yarn模式中就是NodeManager节点】每个Task上——在每一个节点中,有不止一个的Executor【CoarseGrainedExecutorBackend创建】,它是Application运行在Worker上的一个进程【进程名就叫CoarseGrainedExecutorBackend】,每个Executor能并行运行多个Task【线程池中获取,具体数量取决于分配给Executor的CPU core数】。

  2. 最主要的是,各个远程节点上变量的更新并不会传播回 driver program(驱动程序)通用且支持 read-write(读-写) 的共享变量在任务间是低效的。另外,如果变量特别大,一方面会加大网络IO,一方面也会占用太多内存。

  3. 广播变量(Broadcast variables):Spark 会自动广播出每个 stage(阶段)内任务所需要的公共数据。这种情况下广播的数据使用序列化的形式进行缓存,并在每个任务运行前进行反序列化。这也就意味着,只有在跨越多个 stage(阶段)的多个任务会使用相同的数据,或者在使用反序列化形式的数据特别重要的情况下,使用广播变量会有比较好的效果。

    广播变量通过在一个变量 v 上调用 SparkContext.broadcast(v) 方法来进行创建。广播变量是 v 的一个 wrapper(包装器),可以通过调用 value 方法来访问它的值。

    创建广播变量之后,集群中执行的所有的函数中,都应该使用该广播变量代替原来的 v 值。【注意,该广播变量不能修改,只读

  4. 累加器(Accumulators,在util包中):推荐使用2.0之后新的累加器API——AccumulatorV2

    Accumulators(累加器)是一个仅可以通过一个关联和交换操作执行 “added”(添加)的变量,因此可以高效地支持并行。累加器可以用于实现 counter( 计数,类似在 MapReduce 中那样)或者 sums(求和)。原生 Spark 支持数值型的累加器,并且程序员可以添加新的支持类型。

    可以通过调用 SparkContext.longAccumulator() 或 SparkContext.doubleAccumulator() 方法创建数值类型的 accumulator(累加器)以分别累加 Long 或 Double 类型的值。集群上正在运行的任务就可以使用 add 方法来累计数值。然而,它们不能够读取它的值。只有 driver program(驱动程序)才可以使用 value 方法读取累加器的值。
    ##学习中(领悟)

package org.apache.spark.examples

import org.apache.spark.sql.SparkSession

/**
  * Usage: BroadcastTest [partitions] [numElem] [blockSize]
  */
object BroadcastTest {
  def main(args: Array[String]) {

    val blockSize = if (args.length > 2) args(2) else "4096" // 块大小

    val spark = SparkSession
      .builder()
      .master("local")
      .appName("Broadcast Test")
      .config("spark.broadcast.blockSize", blockSize) // 设置blocksize参数
      .getOrCreate()

    val sc = spark.sparkContext

    val slices = if (args.length > 0) args(0).toInt else 2
    val num = if (args.length > 1) args(1).toInt else 1000000

    val arr1 = (0 until num).toArray

    for (i <- 0 until 3) {
      println("Iteration " + i)
      println("===========")
      val startTime = System.nanoTime
      val barr1 = sc.broadcast(arr1) // 使用broadcast方法广播arr1
      // 使用.value获取值【barr1产生之后,它就代替了之前的变量arr1】
      // slice为Worker的数量,这里会为每个Worker复制一份barr1
      val observedSizes = sc.parallelize(1 to 10, slices).map(_ => barr1.value.length)
      // Collect the small RDD so we can print the observed sizes locally.
      observedSizes.collect().foreach(i => println(i))
      println("Iteration %d took %.0f milliseconds".format(i, (System.nanoTime - startTime) / 1E6))
    }

    spark.stop()
  }
}

##学习后(实操):TODO
##总结

  1. 什么时候用广播变量
    广播变量可以用来更新一些大的配置变量,比如数据库中的一张表格。进一步可以用于监控及更新数据。

    只有在跨越多个 stage(阶段)的多个任务会使用相同的数据,或者在使用反序列化形式的数据特别重要的情况下,使用广播变量会有比较好的效果。

  2. 什么时候用累加器:累加器的一个常见的用途是在调试时对作业的执行过程中事件进行计数。

  3. 什么时候不适合用:TODO

  4. 如何修改广播变量:
    Spark会自动监视每个节点上的高速缓存使用情况,并以最近最少使用(LRU)方式删除旧的数据分区。

    如果您想要手动删除RDD,而不是等待其从缓存中删除,请使用RDD.unpersist()方法

##参考

  1. Spark技术内幕:Executor分配详解
  2. 官网共享变量doc
  3. Spark踩坑记——共享变量-博客园
  4. Spark共享变量-累加器和广播变量-简书

你可能感兴趣的:(examples,源码,共享变量,Hadoop,Spark,Hbase...)