大数据开发之Spark篇----spark-core入门(4)

RDD的操作

RDD的Persistence

官网的具体网址:https://spark.apache.org/docs/latest/rdd-programming-guide.html#rdd-persistence
我们已经知道RDD的transformation是一个lazy操作,只有当遇到一个action时才会触发真正的代码执行。但是我们平时所写的代码中我们主要都是一些transformation操作,当要写action操作的时候已经是将结果送回到driver段去了。同时每一次transformation操作都构建一种依赖关系,如果我们的partition数据丢了或者经过一次shuffle需要大量性能去计算而我们不想再做一次但是又有其他的操作依赖于个shuffle的结果,难道我们每一次都从读取数据那一刻开始执行整个流程吗?显然这样会很耗我们的计算资源的,那么我们可以将中间那些我们要重用的数据缓存下来,以方便我们多次使用(当然,你的内存是要足够多的)。
这里给一个生产过程中的案例:我们需要读取一个超大的日志文件,其维度可能达到数百以上,但我们所要到的业务字段只是需要几十个的时候,我们就需要将表裁剪,然后我们会将这个数据缓存下来,以后所有的操作都是从内存中读取这个经过裁剪的小表来用。
这里有两个方法:cache和persist,但这两个操作是lazy的,只有遇到action操作时才会在依赖传输的过程当中执行。并且cache底层其实是调用了persist这个方法的,而这个方法也是一个对persist带参数的方法的一个重载。图中,下面的cache方法是调用了上面persist的方法,而这个persist方法是调用了带参的persist方法的。
在这里插入图片描述

//将一个RDD缓存下来
val rdd = sc.textFile("...").map(...)
rdd.persist()
rdd.count()

这个操作是eager的,马上就执行得

//将上面的缓存清除掉
rdd.unpersist()

既然是缓存数据下来,那么有不同的缓存等级的:在这里插入图片描述
这里我们可以看到不同的缓存等级,以及是否使用序列化,使用了序列化将会一定程度上方便存储的。
我们使用这个操作,需要输入一个参数来确定一个存储级别,后面一幅图我们来看下这个参数的底层是怎么样的。
在这里插入图片描述
在这里插入图片描述
其实就是构建了一个StorageLevel的伴生对象,在里面定义了一些变量,而这些变量是有StorageLevel的伴生类new出来了,里面需要传入5个参数:

  1. 是否使用磁盘存储
  2. 是否使用内存存储
  3. 是否使用堆外内存来存储
  4. 是否使用反序列,就是当为true的时候就是不序列化对象了
  5. 缓存的数据的额副本系数是多少,默认指是1
    当我们使用序列化的时候我们可以节省掉一部分内存、磁盘、网络的开销,但是会增加cpu的性能开销。并且在生产上我们要尽量避免将数据写入到磁盘当中去,我们其实可以只考虑使用MEMORY_ONLY和MEMORY_ONLY_SER这两种选项,就是是否使用序列化,你要权衡一下现在内存和cpu之间的开销问题了。

共享变量

网址:https://spark.apache.org/docs/latest/rdd-programming-guide.html#shared-variables

广播变量

什么是广播变量呢?我们知道我们所使用的RDD数据其实是一个个partition,而这些partition是存储在worker node当中,就是在executor端的,但是如果我们在计算的时候我们需要用到一些在driver端创建的变量值,那么我们每次计算一个partition中的一个元素时都要将这个变量从driver端发送到executor端去,如果我们的RDD里面有10000个元素就要发送10000次了,这样我们的网络开销不是一般的大呀,但如果我们只有两个executor端,那么我们只需将变量分发到两个executor就可以了,每个executor上的partition都可以共享这个executor上面的这个变量了。
如果我们是对两个表进行join操作,而其中一个表是一个小表(数据量很少的话),我们其实可以将小表的数据从一个rdd当中抽取出来当作一个普通数据类型的对象,然后广播到各个worker node上去,这样我们还可以避免shuffle操作。

下面给一个简单的例子:

object test {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    val sc = new SparkContext(conf)
    val t1 = sc.parallelize(Array((1,"xiaoming"),(2,"xiaoqiang"),(3,"asha"))).map(x => (x._1,x._2))
    val t2 = sc.parallelize(Array((1,18),(3,20))).map(x => (x._1,x._2))
    val end = t1.join(t2)
    end.collect()
    sc.stop()
  }
}

这个运行的结果我们在SparkUI上面看看:
是有shuffle的
从DAG图上我们可以看到是有shuffle的
我们再看看另外一种使用了广播变量的方式:

object test {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    val sc = new SparkContext(conf)
    val t1 = sc.parallelize(Array((1,"xiaoming"),(2,"xiaoqiang"),(3,"asha"))).map(x => (x._1,x._2))
    val t2 = sc.parallelize(Array((1,18),(3,20))).map(x => (x._1,x._2)).collectAsMap()
    val t2Bro = sc.broadcast(t2)
    t1.mapPartitions(partition => {
      val t2Map = t2Bro.value
      var list = new ListBuffer[(Int,Int,String)]
      for((key,value) <- partition if t2Map.contains(key)){
        list.append((key,t2Map.getOrElse(key,0),value))
      }
      list.iterator
    }).collect()
    sc.stop()
  }
}

在这里插入图片描述在这里插入图片描述
虽然我们用了两个job,但是我们没有见到shuffle呀,这样性能就可以极大地提升了。

累加器

因为广播变量是一个只读的变量,我们如果我们需要一个可以操作的用来计算每一个partition中数量的变量时我们可以使用计算器

//这个累加器是从0开始加的,而类机器里面所传入的参数是它的名字
val accum = sc.longAccumulator("my accumulator")
//取出累加器的数据
Long end = accum.value

Spark on Yarn

之前我们一直是使用local模式来运行,但在生产当中我们都是使用yarn模式的,基本不用standalone模式的。其实就是我们的Spark作业的clusterManager是使用yarn。
在spark on yarn这种情况下,有两种模式:

  1. client :driver进程是在我们提交作业的机器上的
  2. cluster :driver进程实在集群里面的,只在ApplicationMaster所在节点上
    当我们是指定 --master yarn,而没哟指定–deploy-mode时,系统默认是client模式。

cluster模式下:
我们从client机上提交一个作业上去,然后YARN会为这个作业分配一个container用来转载我们的ApplicationMaster,同时driver进程也会在这个NodeManager节点上,然后我们的AM会向YARN的RM申请资源了,当拿到这些资源列表后在去和其他NM通信要求它们启动相应的container来承载executor进程。这时AM课NM通信来确保container里的情况,而driver进程来和executor保持通信来传送代码和task。
client模式下:
spark的driver将不在YARN上的AM里面,而是在我们的客户机上,那么集群里面的executor就要实时和客户机保持联系了,所以我们的客户机是不能直接crtl + z退出进程的,其他和cluster是一样的。

另外,driver在哪里,那么spark的日志将会产生在哪里,所以client更方便我们查看日志。如果是cluster模式,我们只能通过YARN的UI界面(8080端口)获取到applicationId后在集群里面使用命令:

yarn logs -applicationId ${application的id}

来获取spark作业的信息,当然你也可以点YARN的UI界面相应的application列表后面的History这个按钮进入到SparkUI界面去查看Spark作业。

猜你喜欢

转载自blog.csdn.net/weixin_39702831/article/details/85237637