6.Spark Core 应用解析之RDD持久化

1.概述

Spark是分布式基于内存的数据处理引擎,它的一个基本功能是将RDD持久化到内存中。巧妙使用RDD持久化,甚至在某些场景下,可以将spark应用程序的性能提升10倍。对于迭代式算法和快速交互式应用来说,RDD持久化,是非常重要的。

Spark中最重要的功能之一是操作时在内存中持久化(缓存)数据集。默认情况下当使用action 在RDD上时Spark会重新计算刷新RDD.但也可以通过持久化方法cache和persist将RDD放在内存当中。这样当第二次使用action 在RDD上时,Spark将不重新计算刷新RDD

一个简单例子

scala> val wordcountRDD = sc.textFile("hdfs://harvey:9000/wordcount/input").flatMap(_.split(" ")).map((_, 1)).reduceByKey(_+_)
rdd: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[209] at reduceByKey at <console>:24

scala> wordcountRDD .persist()
res129: rdd.type = ShuffledRDD[209] at reduceByKey at <console>:24

scala> wordcountRDD .count()
res130: Long = 6

# 第二次wordcountRDD.count(),不会再重新计算刷新RDD
scala> wordcountRDD.count()
res131: Long = 6

scala> wordcountRDD.collect
res132: Array[(String, Int)] = Array((scala,1), (spark,1), (hadoop,1), (python,1), (hello,5), (java,1))

上述示例中,首先从HDFS文件中获取数据,然后进行flatMap、map、reduceByKey算子操作计算统计单数出现的次数得出wordcountRDD,并将该RDD持久化到了内存,之后两次Action count操作,只有第一次的时候才会执行做计算操作,而第二次count不会再重新计算刷新RDD,直接从内存中获取数据

2.持久化(缓存)

RDD的持久化或缓存选项是通过persist()或cache()发出的,之后如果某个操作(Action)触发该RDD的数据第一次被计算,那么计算的结果数据(也就是该RDD的数据)就会以分区的形式被缓存于计算节点的内存中;而且这些数据是可以实现容错的,如果这个RDD的某些分区数据丢失(因为节点故障),这些分区的数据可以在使用时通过世代信息(Lineage)被自动恢复。

cache() 源码:

// RDD.scala 部分源码
/**
 * Persist this RDD with the default storage level (`MEMORY_ONLY`).
 */
def cache(): this.type = persist()

/**
 * Persist this RDD with the default storage level (`MEMORY_ONLY`).
 */
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)

persist() 源码:

// RDD.scala 部分源码
/**
 * Set this RDD's storage level to persist its values across operations after the first time
 * it is computed. This can only be used to assign a new storage level if the RDD does not
 * have a storage level set yet. Local checkpointing is an exception.
 */
def persist(newLevel: StorageLevel): this.type = {
  if (isLocallyCheckpointed) {
    // This means the user previously called localCheckpoint(), which should have already
    // marked this RDD for persisting. Here we should override the old storage level with
    // one that is explicitly requested by the user (after adapting it to use disk).
    persist(LocalRDDCheckpointData.transformStorageLevel(newLevel), allowOverride = true)
  } else {
    persist(newLevel, allowOverride = false)
  }
}

从Spark的源码中可以看出,cache()和persist()的区别在于,cache()是persist()的一种简化方式,cache()的底层就是调用的persist()的无参版本,也就是调用persist(MEMORY_ONLY),将数据持久化到内存中,persist()方法可以手工设定StorageLevel来满足工程需要的存储级别。如果需要从内存中清除缓存,使用rdd.unpersist()方法。注意cache或者persist并不是action

3.存储级别

RDD的存储形式或存储介质是可以通过存储级别(Storage Level)被定义的,源码如下

// StorageLevel.scala部分源码
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)
   .
   .
   .

从源码中可以看到StorageLevel 中共列出了12中缓存级别,并且每个缓存级别后面都跟了一个StorageLevel 的构造函数,构造函数参数中有的是4个,有的是5个,查看源码如下

/**
 * :: DeveloperApi ::
 * Flags for controlling the storage of an RDD. Each StorageLevel records whether to use memory,
 * or ExternalBlockStore, whether to drop the RDD to disk if it falls out of memory or
 * ExternalBlockStore, whether to keep the data in memory in a serialized format, and whether
 * to replicate the RDD partitions on multiple nodes.
 *
 * The [[org.apache.spark.storage.StorageLevel]] singleton object contains some static constants
 * for commonly useful storage levels. To create your own storage level object, use the
 * factory method of the singleton object (`StorageLevel(...)`).
 */
@DeveloperApi
class StorageLevel private(
    private var _useDisk: Boolean,
    private var _useMemory: Boolean,
    private var _useOffHeap: Boolean,
    private var _deserialized: Boolean,
    private var _replication: Int = 1)
  extends Externalizable {
   .
   .
   .

从上述源码中可以看到StorageLevel 类的主构造器有五个参数,分别如下
useDisk:使用磁盘
useMemory:使用内存
useOffHeap:使用堆外内存,这是Java虚拟机里面的概念,堆外内存意味着把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机)。这样做的结果就是能保持一个较小的堆,以减少垃圾收集对应用的影响
deserialized:反序列化,其逆过程序列化(Serialization)是java提供的一种机制,将对象表示成一连串的字节;而反序列化就表示将字节恢复为对象的过程。序列化是对象永久化的一种机制,可以将对象及其属性保存起来,并能在反序列化后直接恢复这个对象
replication:备份数(在多个节点上备份)

如下,表示将RDD缓存到内存及磁盘中,并且在多个节点上备份2份(默认只有一份)

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

持久化级别总结如下
在这里插入图片描述

注意只能设置一种存储级别,不然会抛异常: Cannot change storage level of an RDD after it was already assigned a level

4.选择存储级别

Spark提供的多种持久化级别,主要是为了在CPU和内存消耗之间进行取舍。下面是一些通用的持久化级别的选择建议:
1、优先使用MEMORY_ONLY,如果可以缓存所有数据的话,那么就使用这种策略。因为纯内存速度最快,而且没有序列化,不需要消耗CPU进行反序列化操作。

2、如果MEMORY_ONLY策略,无法存储的下所有数据的话,那么使用MEMORY_ONLY_SER,将数据进行序列化进行存储,纯内存操作还是非常快,只是要消耗CPU进行反序列化。

3、如果需要进行快速的失败恢复,那么就选择带后缀为_2的策略,进行数据的备份,这样在失败时,就不需要重新计算了。

4、能不使用DISK相关的策略,就不用使用,有的时候,从磁盘读取数据,还不如重新计算一次。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值