Spark中cache和persist的作用以及存储级别

本文详细介绍了在Spark中如何通过RDD持久化来提高程序效率,探讨了多种持久化级别及其适用场景,包括MEMORY_ONLY、MEMORY_AND_DISK等,并提供了选择建议。

在Spark中有时候我们很多地方都会用到同一个RDD, 按照常规的做法的话,那么每个地方遇到Action操作的时候都会对同一个算子计算多次,这样会造成效率低下的问题

例如:

val rdd1 = sc.textFile("xxx")
rdd1.xxxxx.xxxx.collect
rdd1.xxx.xxcollect

上面就是两个代码都用到了rdd1这个RDD,如果程序执行的话,那么sc.textFile(“xxx”)就要被执行两次,我们可以把rdd1的结果进行cache到内存中,使用如下方法

val rdd1 = sc.textFile("xxx")
val rdd2 = rdd1.cache
rdd2.xxxxx.xxxx.collect
rdd2.xxx.xxcollect

按照如上的方法改造后,后面两个业务代码执行的话rdd1就会在第一次执行的时候被持久化到内存中,后面的业务代码就可以复用,能有效提高效率

其中cache这个方法也是个Tranformation,当第一次遇到Action算子的时才会进行持久化

其中cache内部调用了persist方法,persist方法又调用了persist(StorageLevel.MEMORY_ONLY)方法,所以执行cache算子其实就是执行了persist算子且持久化级别为MEMORY_ONLY

def cache(): this.type = persist()
def persist(): this.type = persist(StorageLevel.MEMORY_ONLY)

注意:


Spark有几种持久化级别如下(参考自博客):

1.MEMORY_ONLY

使用未序列化的Java对象格式,将数据保存在内存中。如果内存不够存放所有的数据,则数据可能就不会进行持久化。那么下次对这个RDD执行算子操作时,那些没有被持久化的数据,需要从源头处重新计算一遍。这是默认的持久化策略,使用cache()方法时,实际就是使用的这种持久化策略。

2.MEMORY_AND_DISK

使用未序列化的Java对象格式,优先尝试将数据保存在内存中。如果内存不够存放所有的数据,会将数据写入磁盘文件中,下次对这个RDD执行算子时,持久化在磁盘文件中的数据会被读取出来使用。

3.MEMORY_ONLY_SER

基本含义同MEMORY_ONLY。唯一的区别是,会将RDD中的数据进行序列化,RDD的每个partition会被序列化成一个字节数组。这种方式更加节省内存,从而可以避免持久化的数据占用过多内存导致频繁GC。

4.MEMORY_AND_DISK_SER

基本含义同MEMORY_AND_DISK。唯一的区别是,会将RDD中的数据进行序列化,RDD的每个partition会被序列化成一个字节数组。这种方式更加节省内存,从而可以避免持久化的数据占用过多内存导致频繁GC。

5.DISK_ONLY

使用未序列化的Java对象格式,将数据全部写入磁盘文件中。

6.MEMORY_ONLY_2, MEMORY_AND_DISK_2, 等等

对于上述任意一种持久化策略,如果加上后缀_2,代表的是将每个持久化的数据,都复制一份副本,并将副本保存到其他节点上。这种基于副本的持久化机制主要用于进行容错。假如某个节点挂掉,节点的内存或磁盘中的持久化数据丢失了,那么后续对RDD计算时还可以使用该数据在其他节点上的副本。如果没有副本的话,就只能将这些数据从源头处重新计算一遍了。

默认情况下,性能最高的当然是MEMORY_ONLY,但前提是你的内存必须足够足够大,可以绰绰有余地存放下整个RDD的所有数据。因为不进行序列化与反序列化操作,就避免了这部分的性能开销;对这个RDD的后续算子操作,都是基于纯内存中的数据的操作,不需要从磁盘文件中读取数据,性能也很高;而且不需要复制一份数据副本,并远程传送到其他节点上。但是这里必须要注意的是,在实际的生产环境中,恐怕能够直接用这种策略的场景还是有限的,如果RDD中数据比较多时(比如几十亿),直接用这种持久化级别,会导致JVM的OOM内存溢出异常。

如果使用MEMORY_ONLY级别时发生了内存溢出,那么建议尝试使用MEMORY_ONLY_SER级别。该级别会将RDD数据序列化后再保存在内存中,此时每个partition仅仅是一个字节数组而已,大大减少了对象数量,并降低了内存占用。这种级别比MEMORY_ONLY多出来的性能开销,主要就是序列化与反序列化的开销。但是后续算子可以基于纯内存进行操作,因此性能总体还是比较高的。此外,可能发生的问题同上,如果RDD中的数据量过多的话,还是可能会导致OOM内存溢出的异常。

如果纯内存的级别都无法使用,那么建议使用MEMORY_AND_DISK_SER策略,而不是MEMORY_AND_DISK策略。因为既然到了这一步,就说明RDD的数据量很大,内存无法完全放下。序列化后的数据比较少,可以节省内存和磁盘的空间开销。同时该策略会优先尽量尝试将数据缓存在内存中,内存缓存不下才会写入磁盘。

通常不建议使用DISK_ONLY和后缀为_2的级别:因为完全基于磁盘文件进行数据的读写,会导致性能急剧降低,有时还不如重新计算一次所有RDD。后缀为_2的级别,必须将所有数据都复制一份副本,并发送到其他节点上,数据复制以及网络传输会导致较大的性能开销,除非是要求作业的高可用性,否则不建议使用。


  • 可以查询RDD的持久化策略:
scala> rdd1.getStorageLevel
res17: org.apache.spark.storage.StorageLevel = StorageLevel(false, false, false, false, 1)

scala> rdd1.getStorageLevel.description
res18: String = Serialized 1x Replicated
  • UI界面上面查看持久化的数据:

当cache后查看持久化的数据:

这里写图片描述

当执行rdd1.unpersist(true)后,持久化的数据就会被删除:

这里写图片描述

<think>我们正在讨论的是Spark中的存储级别(StorageLevel),而不是Scala伴生对象或类。用户可能混淆了“Scala”Spark”的上下文,但根据引用[3](RDD.scala源码)用户之前的问题,实际关注的是Spark RDD的存储级别。 用户当前问题:“我想了解Scala中存储级别作用” 注意:存储级别Spark中的概念,不是Scala语言本身的概念。但Spark使用Scala编写,所以我们在Scala代码中使用这些存储级别。 引用[3]显示:RDD的存储级别可以通过persist方法设置,默认是MEMORY_ONLY。 因此,回答应聚焦于Spark RDD存储级别作用存储级别作用:控制RDD(或DataFrame/Dataset)在计算过程中的存储方式,以平衡计算性能资源使用(内存、磁盘)。通过缓存数据,避免重复计算,提高性能。 存储级别由以下几个因素组合而成: - 是否使用内存(堆内或堆外) - 是否使用磁盘 - 是否序列化(减少内存占用,但增加CPU开销) - 是否存储多个副本(提高容错性) 常见的存储级别(在org.apache.spark.storage.StorageLevel中定义): - MEMORY_ONLY:反序列化对象存储在JVM内存中。如果内存不足,部分分区不会被缓存,每次需要时重新计算。 - MEMORY_AND_DISK:内存不足时,溢写到磁盘。 - MEMORY_ONLY_SER:序列化后存储在内存(每个分区一个字节数组),节省空间但增加CPU反序列化开销。 - MEMORY_AND_DISK_SER:序列化存储,内存不足时溢写到磁盘。 - DISK_ONLY:只存储在磁盘上。 - 还有带“_2”的级别(如MEMORY_ONLY_2)表示每个分区在两个节点上存储副本,提高容错性。 - OFF_HEAP:使用堆外内存(例如Tachyon或堆外内存管理),数据以序列化格式存储存储级别作用具体包括: 1. 加速迭代计算:避免重复计算,特别是多次使用同一个RDD时。 2. 资源管理:根据内存磁盘资源情况,选择最合适的存储方式,避免内存溢出或磁盘空间不足。 3. 容错性:通过多副本存储,防止节点崩溃导致数据丢失(但重新计算也是一种容错方式)。 4. 性能优化:序列化减少内存占用,但增加CPU开销,需要权衡。 例如,在多次使用同一个RDD的迭代算法(如机器学习训练)中,将RDD缓存可以显著提高性能。 代码示例(使用引用[3]中的persist方法): ```scala import org.apache.spark.storage.StorageLevel val rdd = sc.parallelize(1 to 1000000) // 设置存储级别为MEMORY_ONLY_SER rdd.persist(StorageLevel.MEMORY_ONLY_SER) // 或者使用cache(),等同于persist(StorageLevel.MEMORY_ONLY) rdd.cache() ``` 注意事项: - 缓存数据会占用内存或磁盘,需要根据集群资源合理选择。 - 如果内存不足且使用MEMORY_ONLY,可能会频繁重新计算,影响性能;此时可考虑MEMORY_AND_DISK,但会增加磁盘IO。 - 序列化级别可以减少内存使用,但会消耗CPU进行序列化反序列化。 因此,存储级别的选择需要在内存使用、磁盘使用、CPU开销容错性之间做权衡。 根据用户前一个问题,他曾遇到使用MEMORY_ONLY_SER导致磁盘空间不足的问题,但实际MEMORY_ONLY_SER不会使用磁盘,所以可能他之前使用的是MEMORY_AND_DISK_SER?或者有其他误解?但本问题只问作用,所以可以简单带过。 生成相关问题: 1. 如何选择Spark中合适的存储级别? 2. Spark存储级别如何影响计算性能? 3. 如何监控Spark中缓存数据的内存使用情况? </think> ### Scala中存储级别作用Spark框架中,存储级别(StorageLevel)是控制RDD(Resilient Distributed Datasets)数据持久化策略的核心机制,它决定了数据如何在内存磁盘之间存储管理。主要作用包括: #### 1. **性能优化** - **减少重复计算**:通过缓存中间结果,避免相同计算的重复执行[^3] - **序列化压缩**:`MEMORY_ONLY_SER`等级别通过序列化减少内存占用(约2-5倍),提升内存利用率[^3] - **数据定位优化**:根据访问频率决定数据存储位置(内存/磁盘) #### 2. **资源管理** - **内存控制**:`MEMORY_ONLY`仅使用内存,避免磁盘I/O - **磁盘兜底**:`MEMORY_AND_DISK`在内存不足时溢出到磁盘 - **堆外内存**:`OFF_HEAP`避免JVM垃圾回收开销[^3] #### 3. **容错机制** - **多副本存储**:`_2`后缀的级别(如`MEMORY_ONLY_SER_2`)在不同节点存储副本 - **故障恢复**:当节点失效时直接从存储中恢复数据,无需重新计算 #### 4. **计算策略控制** - **重新计算vs持久化**:`MEMORY_ONLY`在内存不足时选择重新计算而非使用磁盘 - **存储成本权衡**:根据数据重用频率选择最佳存储策略 #### 存储级别对比表 | 级别 | 内存使用 | 磁盘使用 | 序列化 | 副本数 | 适用场景 | |------|----------|----------|--------|--------|----------| | `MEMORY_ONLY` | 高 | 无 | 否 | 1 | 小数据集/内存充足 | | `MEMORY_ONLY_SER` | 中 | 无 | 是 | 1 | 内存受限场景 | | `MEMORY_AND_DISK_SER` | 中 | 有 | 是 | 1 | 大数据集/需容错 | | `DISK_ONLY` | 无 | 高 | 否 | 1 | 超大数据集 | | `OFF_HEAP` | 中(堆外) | 无 | 是 | 1 | 避免GC暂停 | #### 代码示例 ```scala import org.apache.spark.storage.StorageLevel val rdd = sc.parallelize(1 to 1000000) // 默认存储级别 (MEMORY_ONLY) rdd.cache() // 显式指定序列化存储 rdd.persist(StorageLevel.MEMORY_ONLY_SER) // 带容错的多副本存储 rdd.persist(StorageLevel.MEMORY_ONLY_SER_2) // 使用堆外内存 rdd.persist(StorageLevel.OFF_HEAP) ``` #### 最佳实践 1. 优先使用`MEMORY_ONLY`处理小型数据集 2. 内存紧张时采用`MEMORY_ONLY_SER`平衡空间性能 3. 避免磁盘空间不足时选择`OFF_HEAP`或减少副本数 4. 监控存储情况通过`SparkUI`的`Storage`标签页[^3] > 关键点:存储级别Spark中实现了**计算与存储的解耦**,通过智能持久化策略,在性能、资源利用容错性之间取得平衡[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值