SparkCore(4)

上节课回顾:

(1)Spark on YARN 是不需要启动spark的任何东西的,找台机器布一下Spark就好提交作业。

(2)Spark on YARN的俩种模式的区别。取决于Driver跑在哪里,跑在client就local模式,跑在AM就是yarn

(3)Spark shell不能是cluster模式,当他使用cluster模式,是不是代表了Driver在cluster集群里面,那他和executor如何通信发代码?所以是不行的。

1.collect和take

(1)作用:将数据输出控制台,但是这个德慎用,因为,collect将数据存到driver端,如果数据量太大了,就会出现OOM。

driver的memory默认是一个1G,所以它是由弊端的。可以自己设置,--driver-memory

(2)类似函数:countBykey(求key的数量)countByvaluey(求value的数量)collectAsmap(返回键值对,要是Tulpe)

(3)take是部分扫,不是全部遍历,所以当你不知道数据有多少的时候,就用take抽样数据。

2..count

当你不确定文件的大小时,可以用conut的检测,多大就保存得到文件系统里面去。

3.groupBykey和reduceBykey的区别

reduceBykey:在map端先做预聚合,在shuffer中是聚合之后的在shuffer。数据量小一点。类似于MR里面的combiner

(a,1)   ---->(a,1)  预聚合  (b,1)  -- shuffle->(a,1)  (a,2)

 groupBykey:是直接到shuffer端聚合,shuffer的数据量大一点,效率也会更加低一点。

(a,1) ----shuffle-> (a,1)  (a,1)  (a,1)

4.共享变量

(1)默认情况下,如果在一个算子的函数中使用到了某个外部的变量,那么这个变量的值会被拷贝到每个task中。此时每个task只能操作自己的那份变量副本。如果多个task想要共享某个变量,那么这种方式是做不到的。

Spark为此提供了两种共享变量,一种是Broadcast Variable(广播变量),另一种是Accumulator(累加变量)。Broadcast Variable会将使用到的变量,仅仅为每个节点(executor)拷贝一份,更大的用处是优化性能,减少网络传输以及内存消耗。Accumulator则可以让多个task共同操作一份变量,主要可以进行累加操作。

(2)Accumulators:主要用于多个节点对一个变量进行共享性的操作。Accumulator只提供了累加的功能。但是确给我们提供了多个task对一个变量并行操作的功能。但是task只能对Accumulator进行累加操作,不能读取它的值。只有Driver程序可以读取Accumulator的值。

package SparkReview.Core4

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

object Boradcast {
  def main(args: Array[String]): Unit = {
    val sparkconf=new SparkConf().setAppName("Boradcast").setMaster("local[2]")
    val sc=new SparkContext(sparkconf)
    val accum=sc.longAccumulator("My Accumulator")

    sc.parallelize(Array(1,2,3,2,4,5)).foreach(x=>
      accum.add(x)
    )  //这里foreach换成map为啥不行?
println(accum.value)  //这就是一个累加,在生产中需要用它计数成功了多少条,失败了多少条。


//由于在map等算子内部使用了外部定义的变量和函数,从而引发Task未序列化问题。
    // 然而,Spark算子在计算过程中使用外部变量在许多情形下确实在所难免,比如在filter算子根据外部指定的条件进行过滤,
    // map根据相应的配置进行变换等。为了解决上述Task未序列化问题,这里对其进行了研究和总结。
    sc.stop()
  }

}

(3)Broadcast Variables:会将使用到的变量,仅仅为每个节点(executor)拷贝一份,更大的用处是优化性能,减少网络传输以及内存消耗。不然就只能每个task复制一个,task肯定比executor多很多的

package SparkReview.Core4

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

object BoradCastJoinApp {
  def main(args: Array[String]): Unit = {
    val sparkConf=new SparkConf().setAppName("BoradCastJoinApp").setMaster("local[2]")
    val sc=new SparkContext(sparkConf)
   // commonJoin(sc)
    BroadcastJoin(sc)





    sc.stop()
  }
  def commonJoin(sc:SparkContext):Unit={
    val peopleInfo=sc.parallelize(Array(("G301","糊涂虫"),("G302","僧地"),("G303","Gordon"))).map(x=>(x._1,x))
    val peopleDetail=sc.parallelize(Array(("G301","清华大学",18))).map(x=>(x._1,x))
    //TODO...大表Join小标  join key
    //peopleInfo.join(peopleDetail).foreach(println)//(G301,((G301,糊涂虫),(G301,清华大学,18))).结果明显不大好看
    peopleInfo.join(peopleDetail).map(x=>{
      x._1+","+x._2._1._2+","+x._2._2._2+","+x._2._2._3 //在去多个字段的时候一定要写好注释,不然一觉醒来就忘干净了
    }).foreach(println)

  }
  def BroadcastJoin(sc:SparkContext):Unit={
    val peopleInfo=sc.parallelize(Array(("G301","糊涂虫"),("G302","僧地"),("G303","Gordon"))).map(x=>(x._1,x))
      .collectAsMap()  //广播变量在大多数情况下都要转成collectAsMap
    //DAG图是生活中必须要懂的
    val peopleDetail=sc.parallelize(Array(("G301","清华大学",18))).map(x=>(x._1,x))
    //通过SparkContext将变量广播出去
    val people=sc.broadcast(peopleInfo)
    peopleDetail.mapPartitions(x=>{
      val map=people.value  //得到广播变量的值
      for ((key,value)<-x if (map.contains(key))) //
        yield (key,map.get(key).getOrElse(""),value._2,value._3)   //下面那段话的意义就是,for 循环中的 yield 会把当前的元素记下来,
      // 保存在集合中,循环结束后将返回该集合。Scala 中 for 循环是有返回值的。如果被循环的是 Map,
      // 返回的就是  Map,被循环的是 List,返回的就是 List,以此类推。

    }).foreach(println)
  }

}

5.shuffle和closures

(1)shuffle:它必须从所有分区中读取,以找到所有键的所有值,然后将跨分区的值组合在一起,以计算每个键的最终结果,这就叫shuffle。

(2)closures:跨作用域访问函数变量,又指的一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

package SparkReview.Core4

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

object Closures {
  def main(args: Array[String]): Unit = {
    val sparkconf=new SparkConf().setMaster("local[2]").setAppName("Closures")
    val sc=new SparkContext(sparkconf)

val rdd=sc.parallelize(List(1,2,3))
    var counner=0    //val 不可变, var可变
    //这个函数是在main函数里面的,这个是在Driver端运行的

    rdd.foreach(x =>counner += x)  //这个foreach函数是操作RDD的 ,也就是在worker节点上具体运行的
    println("Counter value :"+ counner )    //输出结果为0
//当worker上的函数调用外面的变量时,就出现了闭包的问题,这时候就出现了广播的概念。


    sc.stop()



  }

}

猜你喜欢

转载自blog.csdn.net/qq_42064119/article/details/82024632