【spark】spark编程坑——foreach的坑


前言

在开发时发现一个事,rdd有foreach方法,rdd.collect之后也有foreach,这两个方法却大不一样。

1.1、代码示例

代码如下(示例):

 def main(args: Array[String]): Unit = {
    
    
    val spark = SparkSession
      .builder
      .master("local[*]")
      .appName("test")
      .getOrCreate()
    val sc = spark.sparkContext;
    val value = sc.makeRDD(List(1, 2, 3, 4),2)
      .map(_*2);
    value.collect().foreach(println);
    println("**********")
    value.foreach(println)
    sc.stop()
    }

输出:

2
4
6
8
**********
6
8
2
4

在本地为多线程(local[*])执行的时候,并且分区为2个以上的时候,rdd.foreach方法打印出来的数据顺序乱了!~

2.1、解释

在这里插入图片描述
如图所示,rdd.foreach方法在执行的过程中的打印方法是在Executor中执行的,每个Executor在执行完自己的逻辑之后就执行foreach进行打印,因此在本地多线程执行的时候,可能List(3,4)是有可能先执行完成,所以会存在顺序错乱的情况。
多线程,多分区才会有这种情况
value.collect().foreach(println)这种写法的print是在数据采集到Driver之后,在Driver端打印的。所以顺序不会乱。

2.1 代码示例

RDD中集合有1,2,3,4,想通过foreach来进行累加,但是打印出来的结果却是0。这个就是累加器的坑。大家多多参考。


  def main(args: Array[String]): Unit = {
    
    
    val spark = SparkSession
      .builder
      .master("local[*]")
      .appName("test")
      .getOrCreate()
    val sc = spark.sparkContext;

    val value = sc.makeRDD(List(1, 2, 3, 4))

    var sum = 0;
    value.foreach(num=>{
    
    
      sum+=num;
    })
    println("总和为:"+sum)
    sc.stop()

}

输出:

总和为:0

在普通的java编程中或者其他语言的变成中, 这种写法是没有问题的,但是在RDD中,有Driver和Executor的概念,RDD的计算是在Executor中进行的。然而sum确是在Driver中产生的。
在这里插入图片描述
如上图所示,sum=0 会传递到两个Executor中,进行累加,两个Executor的sum没有各自累加自己的数据,但是sum的值并没有回调给Driver中,因此,Driver中的sum一直是0。
因此spark数据结构中有一个叫累加器。通过累加器即可完成此功能,代码如下:

  def main(args: Array[String]): Unit = {
    
    
    val spark = SparkSession
      .builder
      .master("local[*]")
      .appName("test")
      .getOrCreate()
    val sc = spark.sparkContext;
    val value = sc.makeRDD(List(1, 2, 3, 4))
    val sum =    sc.longAccumulator("sum");
    value.foreach(num=>{
    
    
      sum.add(num)
    })
    println("总和为:"+sum.value)
    sc.stop()
}

猜你喜欢

转载自blog.csdn.net/qq_30285985/article/details/110953259