【大数据学习】之 Spark-RDD core4

SparkCore04

一、RDD Persistence简介。指RDD持久化,据官网的解释:

Spark 中一个很重要的能力是将数据持久化(或称为缓存),在多个操作间都可以访问这些持久化的数据。当持久化一个 RDD 时,每个节点的其它分区都可以使用 RDD 在内存中进行计算,在该数据上的其他 action 操作将直接使用内存中的数据。这样会让以后的 action 操作计算速度加快(通常运行速度会加速 10 倍)。缓存是迭代算法和快速的交互式使用的重要工具。

RDD 可以使用 persist() 方法或 cache() 方法进行持久化。数据将会在第一次 action 操作时进行计算,并缓存在节点的内存中。Spark 的缓存具有容错机制,如果一个缓存的 RDD 的某个分区丢失了,Spark 将按照原来的    计算过程,自动重新计算并进行缓存。

cache的特性:在rdd中是 lazy的,即延迟执行,或懒的,因为它只会在遇到action才会真的计算,但是在spark sql中是eager的,即立即执行的。

cache 和persist的关系

简单来说,cache()-->persist()-->persist(StorageLevel.MEMORY_ONLY)

 

二、缓存策略

1. Cache级别

Storage Level

Meaning

MEMORY_ONLY

Store RDD as deserialized Java objects in the JVM. If the RDD does not fit in memory, some partitions will not be cached and will be recomputed on the fly each time they're needed. This is the default level.

MEMORY_AND_DISK

Store RDD as deserialized Java objects in the JVM. If the RDD does not fit in memory, store the partitions that don't fit on disk, and read them from there when they're needed.

MEMORY_ONLY_SER 
(Java and Scala)

Store RDD as serialized Java objects (one byte array per partition). This is generally more space-efficient than deserialized objects, especially when using a fast serializer, but more CPU-intensive to read.

MEMORY_AND_DISK_SER 
(Java and Scala)

Similar to MEMORY_ONLY_SER, but spill partitions that don't fit in memory to disk instead of recomputing them on the fly each time they're needed.

DISK_ONLY

Store the RDD partitions only on disk.

MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc.

Same as the levels above, but replicate each partition on two cluster nodes.

OFF_HEAP (experimental)

Similar to MEMORY_ONLY_SER, but store the data in off-heap memory. This requires off-heap memory to be enabled.

 

先来看看Spark作业的执行流程

上图为Spark作业的执行流程,一般是input文件,经过一系列的transformation,最后经过action。从头加载数据,是指从input端加载,假如有100G的数据,每次都从磁盘加载,这样就不切合实际,应该缓存起来,再复用,这样就可以让将来的action运行得更快。cache是一个非常重要的算法。一般是有多个计算要复用到,才需要用到cache。

一定要触发action才会把数据丢到内存里面

 

原来是19M,运行info.count后就变大了,变成40M了,如果解决呢,我们在后面讲。

2、RDD移除

Spark自动监视每个节点上的缓存使用情况,并以最近最少使用(LRU)的方式删除旧数据分区。如果您想手动删除RDD,而不是等待它从缓存中取出,请使用RDD.unpersist()方法。

upersisteager,懒加载,即延迟加载。

比如像删除上面那个原来是19M,运行action后变成40M的,就直接用upersist()。

 

3、persist

StorageLevel的4个参数

object StorageLevel {

  val NONE = new StorageLevel(false, false, false, false)

  val DISK_ONLY = new StorageLevel(true, false, false, false)

  val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)

  val MEMORY_ONLY = new StorageLevel(false, true, false, true)

  val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)

  val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)

  val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)

  val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)

  val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)

  val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)

  val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)

  val OFF_HEAP = new StorageLevel(true, true, true, false, 1)

 

persist的StorageLevel的4个参数代表的意思:

class StorageLevel private(

    private var _useDisk: Boolean,      //使用磁盘

    private var _useMemory: Boolean,    //使用内存

    private var _useOffHeap: Boolean,   //使用对外

    private var _deserialized: Boolean, //不序列化

    private var _replication: Int = 1)  //备份

 

前面的例子是用了cache的,cache默认是使用MEMORY_ONLY的,但是cache是没有办法传参数的,如果想传参数,那应该使用persist,那现在我们修改一下,

先导入包,再执行persist(StorageLevel.MEMORY_ONLY_SER),然后再count一下。

现在刷新一下UI页面,看到size in memory是18.4M。

StorageLevel.MEMORY_ONLY_SER是使用序列化的,序列化是会减少内存的开销的,但是CPU的负载会变高。如果集群里的core够的话可以使用序列化。

 

如何选择哪个存储级别?

Spark的存储级别,在内存的占用和CPU的高效率之间的作出权衡:

a.如果您的RDDs与默认存储级别(MEMORY_ONLY)非常匹配,那么就保留它们。这是cpu效率最高的选项,允许rdd上的操作尽可能快地运行。

b.如果不是,尝试使用MEMORY_ONLY_SER并选择一个快速序列化库,以使对象更节省空间,但访问速度仍然相当快。

c.除非计算数据集的函数很昂贵,或者它们过滤大量数据,否则不要将数据写到磁盘。否则,重新计算分区可能与从磁盘读取分区一样快。

d.如果希望快速恢复错误(例如,如果使用Spark来服务来自web应用程序的请求),请使用复制的存储级别。所有的存储级别都通过重新计算丢失的数据提供完全的容错能力,但是复制的存储级别允许您继续在RDD上运行任务,而不必等待重新计算丢失的分区。

 

三、共享变量

共享变量是非常有用的,广播变量一定要掌握的。

  1. 广播变量--broadcast variables

特性:a.read-only,即只读

     b.cached on each machine

        c.不能广播过大的数据,因为广播的数据是放到executor端的内存中,如果过大, 机器内存会挂

def commonJoin(sc:SparkContext): Unit ={

    //g5 join f11 on g5.id=f11.id 结果应该是:34,进取,深圳,18

    // 构建两个数据集

    val g5=sc.parallelize(Array(("1", "豆豆"), ("2", "牛哥"), ("34", "进取"))).map(x=>(x._1,x)) //要join必须要K V 形式,所以需要用map做一下转换

    val f11=sc.parallelize(Array(("34","深圳",18),("10","北京",2))).map(x=>(x._1,x))



   g5.join(f11).map(x=>{x._1+","+x._2._1._2+","+x._2._2._2+","+x._2._2._3}).foreach(println)

//                             获取K      获取名字        获取城市        获取年龄

 }

 

也可以在控制台上查看数据结构

查看一下输出结果

再在UI页面看看

产生的shuffle

 

现在用广播变量的方式实现:

def broadcastJoin(sc:SparkContext): Unit ={

  //假设g5是个小表

  val g5 = sc.parallelize(Array(("1", "豆豆"), ("2", "牛哥"), ("34", "进取"))).collectAsMap()

  val g5Broadcast=sc.broadcast(g5) //广播小表



  val f11 = sc.parallelize(Array(("34", "深圳", 18), ("10", "北京", 2))).map(x=>(x._1,x))



  f11.mapPartitions(partition=>{

    val g5Stus=g5Broadcast.value //获取广播里面的内容

    for((key,value)<- partition if (g5Stus.contains(key)))

      yield(key,g5Stus.get(key).getOrElse(""),value._2)//yield的主要作用是记住每次迭代中的有关值,并逐一存入到一个数组中

    })



}

在控制台spark-shell输出

来看看UI的Stage

并没有shuffle

如图所示,可以看到避免了shuffle的过程,很好的解决了数据倾斜。

 

但是要注意:不能广播过大的数据。

 

四、Accumulators 

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

下面的代码展示了一个 accumulator(累加器)被用于对一个数字中的元素求和,

 

五、Spark运行模式

Spark On Yarn,生产中经常用到的就是spark on yarn模式。

 

封装成shell脚本

[hadoop@hadoop001 shell]$ vi log-yarn.sh

${SPARK_HOME}/bin/spark-submit \

--master yarn \

--class com.ruozedata.bigdata.core02.LogServerApp \

--name LogServerApp \

/home/hadoop/lib/g5-spark-1.0.jar \

hdfs://hadoop001:9000/logs/input/ hdfs://hadoop001:9000/logs/output/

 

试试提交一下spark on yarn

./log-yarn.sh运行时,报错

Exception in thread "main" java.lang.Exception: When running with master 'yarn' either HADOOP_CONF_DIR or YARN_CONF_DIR must be set in the environment.

原来在官网中有明确的指出需要指定配置文件的位置,注意需要指定HADOOP_CONF_DIR 或者YARN_CONF_DIR

所以完整的脚本是:

export HADOOP_CONF_DIR=/home/hadoop/app/hadoop-2.6.0-cdh5.7.0/etc/hadoop

${SPARK_HOME}/bin/spark-submit \

--master yarn \

--class com.ruozedata.bigdata.core02.LogServerApp \

--name LogServerApp \

/home/hadoop/lib/g5-spark-1.0.jar \

hdfs://hadoop001:9000/logs/input/ hdfs://hadoop001:9000/logs/output/

 

原来Spark On Yarn有两种执行模式,分别是clientcluster,前面介绍的是属于client模式,那cluster是怎样的呢?其实就是:

export HADOOP_CONF_DIR=/home/hadoop/app/hadoop-2.6.0-cdh5.7.0/etc/hadoop

${SPARK_HOME}/bin/spark-submit \

--master yarn \

--deploy-mode cluster \

--class com.ruozedata.bigdata.core02.LogServerApp \

--name LogServerApp \

/home/hadoop/lib/g5-spark-1.0.jar \

hdfs://hadoop001:9000/logs/input/ hdfs://hadoop001:9000/logs/output/

 

Spark On Yarn两种模式的区别,其中最主要的就是driver端在哪里,

       client

              Driver: client   jps

              不能断

              logs

              网络

       cluster

              Driver: AM   jps

              能断

              logs看不到

              网络

 

另外,需要知道Spark on yarn的执行流程和MapReduce的执行流程

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值