Spark缓存 之 Collect Cache Persist

不打扰是莪最后的温柔 提交于 2020-10-05 14:31:56

Spark缓存 之 Collect Cache Persist

 

三者都有汇聚数据,拉取数据存储的作用,mark一下各自的作用。

Collect:

  /**
   * Return an array that contains all of the elements in this RDD.
   *
   * @note This method should only be used if the resulting array is expected to be small, as
   * all the data is loaded into the driver's memory.
   */
  def collect(): Array[T] = withScope {
    val results = sc.runJob(this, (iter: Iterator[T]) => iter.toArray)
    Array.concat(results: _*)
  }

collect操作将RDD中所有元素转换为Array,一般多用于本地local模式下测试输出使用;集群模式下不推荐使用,正如源码所说,collect操作应该用于数组预期比较小的情况,因为这里数据会加载到dirver端内存中,本地测试时影响不大,但是集群模式下,如果dirver端内存申请太小就很容易oom。

 

Cache:

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

cache其实就是persist的最基础的一种模式,可以理解为persist的一个多态,因为源码里persist也有这样一个定义:

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

可以看到,调用的cache其实对应的就是无参数的persist,这里使用场景一般是缓存一些多次使用且占用空间较小的RDD,有点类似Map Join广播的小表一样,这里 MEMORY_ONLY代表只存放在内存中,所以需要考虑要缓存的RDD大小。

 

Persist:

  /**
   * Mark this RDD for persisting using the specified level.
   *
   * @param newLevel the target storage level
   * @param allowOverride whether to override any existing level with the new one
   */
  private def persist(newLevel: StorageLevel, allowOverride: Boolean): this.type = {
    // TODO: Handle changes of StorageLevel
    if (storageLevel != StorageLevel.NONE && newLevel != storageLevel && !allowOverride) {
      throw new UnsupportedOperationException(
        "Cannot change storage level of an RDD after it was already assigned a level")
    }
    // If this is the first time this RDD is marked for persisting, register it
    // with the SparkContext for cleanups and accounting. Do this only once.
    if (storageLevel == StorageLevel.NONE) {
      sc.cleaner.foreach(_.registerRDDForCleanup(this))
      sc.persistRDD(this)
    }
    storageLevel = newLevel
    this
  }

persist相对于cache,提供了更灵活的选择:StorageLevel 即储存水平,第二个参数是否允许覆盖是针对spark任务中修改一个RDD的缓存级别,平常用到的机会比较小,大致说一下有哪些存储水平~

 

StorageLevel Class 主类

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有5个构造参数,分别为:

_useDisk : 使用硬盘,可以理解为当RDD太大而内存放不下时,会放在HDFS或者其他存储的位置

_useMemory: 使用内存,cache 和 persist()  就是这种模式

_useOffHeap: 使用堆外内存,JVM还不熟悉,后续深挖一下

_deserialized: 反序列化,可以理解为空间不足或者想节省存储空间的做法,所以采用序列化可以缩减内存占用

_replication: 备份数量,这里默认值为1,如果本身任务缓存数据较大,且任务失败再执行的代价比较高,为了提高容错率,可以修改为2,这里常用场景就是大规模任务日志落地时,防止oom,io等错误导致落地失败而再次重启大规模任务而准备

 

StorageLevel Object 静态类

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)

根据上面五个参数,这里静态类给出了多种构造方法,最常用的是MEMORY_ONLY:适用于小数据,可以放在内存中,中大型数据适合MEMORY_AND_DISK_SER,如果任务失败重启代价太高,可以考虑MEMORY_AND_DISK_SER_2。这里序列化会节省空间,但是相对应也会因为序列化和反序列增加cpu的处理时间,因此是MEMORY_AND_DISK_SER还是MEMORY_AND_DISK可以结合不同使用场景灵活操作。

使用方法:

      rdd.persist(StorageLevel.MEMORY_AND_DISK_SER_2)

 

总计下使用场景:

1.本地测试多用于 collect

2.RDD数据量不大 cache

3.RDD数据量较大 Cpu不足 persist(MEMORY_AND_DISK)  重启代价高替换为 MEMORY_AND_DISK_2

4.RDD数据量较大 Cpu充足 persist(MEMORY_AND_DISK_SER) 重启代价高替换为 MEMORY_AND_DISK_SER_2

常用场景就是这些,在一些RDD需要多次复用时可以考虑采用上述操作,但MEMORY模式下容易出现OOM,DISK模式下则会因为磁盘之间IO而增加运行的时长,这些都是需要考虑的元素,最后记得用完RDD之后调用unpersist释放多余的空间。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!