如何拦截驱动程序上累加器的部分更新?

时间:2016-01-26 11:01:11

标签: apache-spark java-8 accumulator

Spark 1.5.1 + Java 1.8

我们正在使用spark将可靠的记录上传到数据库。

Action代码如下所示:

rdd.foreachPartition(new VoidFunction<Iterator<T>>() {

     @Override
     public void call(Iterator<T> iter) {
          //while there are more records perform the following every 1000 records
          //int[] recoords = statement.executeBatch();
          //accumulator.add(recoords.length);
     }
     // ...
} 

在驱动程序节点上有一个监视累加器值的线程。但是值不会更新。在应用程序结束时,它只会更新一次。即使累加器使用了惰性值设置,也应该正确更新,因为我在驱动程序节点线程中定期读取值。

我是否错误地使用了累加器?无论如何,我可以更持续地监控工人的进步吗?

1 个答案:

答案 0 :(得分:3)

您可以监控累加器值但不能连续完成,即在任务完成后更新发生。

虽然累加器被称为共享变量,但实际上并没有共享。每个任务都有自己的累加器,在任务完成后合并。这意味着在任务运行时无法更新全局值。

为了能够看到更新,执行程序的数量必须小于已处理分区的数量(对应于任务的数量)。这样做的原因是当累加器更新发送给驱动程序时引入“障碍”。

例如:

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

object App {
  def main(args: Array[String]) {
    val conf = new SparkConf().setMaster("local[4]")
    val sc = new SparkContext(conf)

    val accum = sc.accumulator(0, "An Accumulator")
    val rdd = sc.parallelize(1 to 1000, 20)

    import scala.concurrent.duration._
    import scala.language.postfixOps
    import rx.lang.scala._

    val o = Observable.interval(1000 millis).take(1000)
    val s = o.subscribe(_ => println(accum.value))
    rdd.foreach(x => {
      Thread.sleep(x + 200)
      accum += 1
    })
    s.unsubscribe
    sc.stop
  }
}

正如您所看到的,每个任务仅更新一次全局值。

如果您按照提供的示例创建命名累加器,您也可以使用Spark UI监视它的状态。只需打开Stages选项卡,导航到特定阶段并检查累加器部分。

  

无论如何,我可以更持续地监察员工的进度吗?

最可靠的方法是通过添加更多分区来增加粒度,但它并不便宜。