Spark缓存—cache

在 Apache Spark 中,缓存(Cache)是一种优化手段,用于将中间计算结果存储在内存(或磁盘)中,以便后续重复使用,从而避免重复计算,提高作业的执行效率。缓存操作是惰性的,只有在触发行动算子时才会真正执行。
Spark 提供了多种缓存方法,其中最常用的是 cache() 和 persist()。
1. cache()
cache() 是 Spark 中最简单的缓存方法,它会将 RDD 数据存储到内存中,使用默认的存储级别(MEMORY_ONLY)。
• 存储级别:默认将数据存储在内存中,以反序列化的 Java 对象形式存储。
• 使用场景:适用于数据量较小且需要多次使用的场景。
• 示例代码:
scala
复制
val sc = new SparkContext("local", "CacheExample")
val data = Array(1, 2, 3, 4, 5)
val distData = sc.parallelize(data, 2) // 将数据并行化为 RDD

distData
.cache() // 缓存 RDD

// 触发行动算子,计算并缓存结果
val sum = distData.reduce(_ + _)
println
(s"Sum: $sum")

// 再次使用 RDD 时,可以直接从缓存中读取,无需重新计算
val count = distData.count()
println
(s"Count: $count")
2. persist(level)
persist() 是更灵活的缓存方法,允许用户指定存储级别。Spark 提供了多种存储级别,可以根据实际需求选择最适合的存储方式。
• 存储级别:
◦ MEMORY_ONLY:将 RDD 以反序列化的 Java 对象形式存储在内存中(默认级别)。
◦ MEMORY_AND_DISK:将 RDD 存储在内存中,如果内存不足,则将多余的数据存储到磁盘中。
◦ MEMORY_ONLY_SER:将 RDD 以序列化的 Java 对象形式存储在内存中(节省内存空间,但访问速度稍慢)。
◦ MEMORY_AND_DISK_SER:将 RDD 以序列化的 Java 对象形式存储在内存中,如果内存不足,则存储到磁盘中。
◦ DISK_ONLY:仅将 RDD 存储在磁盘中。
◦ MEMORY_ONLY_2、MEMORY_AND_DISK_2 等:与上述级别类似,但会在两个节点上进行备份,提高容错性。
• 使用场景:适用于需要根据存储资源和性能需求灵活选择存储方式的场景。
• 示例代码:
scala
复制
val sc = new SparkContext("local", "PersistExample")
val data = Array(1, 2, 3, 4, 5)
val distData = sc.parallelize(data, 2) // 将数据并行化为 RDD

// 使用 MEMORY_AND_DISK 级别缓存 RDD
distData
.persist(StorageLevel.MEMORY_AND_DISK)

// 触发行动算子,计算并缓存结果
val sum = distData.reduce(_ + _)
println
(s"Sum: $sum")

// 再次使用 RDD 时,可以直接从缓存中读取
val count = distData.count()
println
(s"Count: $count")
3. 缓存的注意事项
• 内存管理:Spark 的缓存机制是基于 LRU(最近最少使用)策略的。如果内存不足,Spark 会自动释放最久未使用的缓存数据。用户可以通过 unpersist() 方法手动释放缓存。
◦ 示例代码:
scala
复制
distData.unpersist() // 手动释放缓存
• 序列化与反序列化:使用序列化存储级别(如 MEMORY_ONLY_SER)可以节省内存,但访问速度会稍慢,因为需要进行序列化和反序列化操作。
• 缓存的触发时机:缓存操作是惰性的,只有在触发行动算子时才会真正执行。因此,如果只调用了 cache() 或 persist(),但没有触发行动算子,数据不会真正被缓存。
• 缓存的容错性:Spark 的缓存机制是容错的。如果某个节点的缓存数据丢失,Spark 会重新计算丢失的数据块,而不会影响整个作业的执行。

### Spark 缓存的适用场景及应用案例 #### 适用场景 Spark缓存适用于那些需要反复读取的数据集,尤其是当这些数据集在多个操作之间共享时。通过将中间计算结果存储在内存中,可以显著减少重复计算的时间开销[^2]。 对于迭代算法而言,如机器学习中的梯度下降法,在每次迭代过程中都需要访问相同的数据源;此时利用缓存功能能够有效提升性能。另外,在涉及宽依赖的操作链路里(比如连接不同分区的数据),合理设置缓存同样有助于优化整体作业效率[^3]。 #### 应用案例 ##### 场景一:机器学习模型训练 假设有一个大规模的日志文件用于构建推荐系统模型。在这个过程中,特征提取阶段会频繁扫描原始日志记录来生成样本向量。如果不采取任何措施,默认情况下每一次调用action都会触发完整的转换流水线重新执行一次,这无疑浪费了大量的资源。因此可以在完成初步预处理之后立即对得到的结果进行cache()或persist(StorageLevel.MEMORY_ONLY),从而使得后续多次使用的同一份数据可以直接从内存加载而不是每次都重头再来[^1]。 ```python from pyspark import SparkContext, StorageLevel sc = SparkContext() # 加载并预处理数据 raw_logs = sc.textFile("hdfs://path/to/logs") processed_data = raw_logs.map(lambda line: process_line(line)) # 将处理后的数据缓存在内存中 processed_data.persist(StorageLevel.MEMORY_ONLY) # 开始训练过程... for iteration in range(num_iterations): model.train(processed_data) ``` ##### 场景二:ETL 数据管道 在一个典型的企业级 ETL 流程中,通常会有多个下游任务依赖于同一个上游输入表。如果不对该公共前置步骤做特殊处理,则意味着每当有新的需求加入进来就要再次经历一遍冗长而耗时的过程。借助于Spark 的持久化特性就可以很好地解决这个问题——只要第一次成功获取到目标表格后就立即将其保存下来供其他环节随时调阅即可。 ```sql -- SQL风格的例子展示如何在SQL查询语句里面使用CACHE TABLE命令实现同样的目的 CREATE OR REPLACE TEMP VIEW cleaned_sales AS ( SELECT * FROM sales WHERE status='completed' ); -- 对视图进行缓存 CACHE TABLE cleaned_sales; -- 后续的各种分析都可以基于已经准备好的cleaned_sales来进行 SELECT COUNT(*) FROM cleaned_sales; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值