RDD-缓存

本文介绍了一个使用Apache Spark进行IP访问频率统计的例子。通过读取access.log文件,将数据转换为RDD,并利用map和reduceByKey操作统计每个IP的访问次数。为了提高效率,使用了persist方法缓存中间结果,默认缓存级别为MEMORY_ONLY。最后展示了如何获取访问次数最多和最少的IP。
  /**
   * 统计访问次数最多的ip
   * None     0.244744 s
   * cache    0.126583 s
   * persist  0.132369 s
   * 
   * cache 底层 调用的 是 persist
   * persist 默认的 缓存级别是 StorageLevel.MEMORY_ONLY
   */
  @Test
  def prepare(): Unit = {
    val resouce: RDD[String] = sc.textFile("dataset/access.log")
    val result: RDD[(String, Int)] = resouce.map(item => (item.split(" ")(0), 1))
      .reduceByKey((curr, agg) => curr + agg)
      .persist(StorageLevel.MEMORY_ONLY)

    val max: Array[(String, Int)] = result.sortBy(item => item._2, ascending = false).take(1)
    val min: Array[(String, Int)] = result.sortBy(item => item._2, ascending = true).take(1)
    max.foreach(println(_))
    min.foreach(println(_))
  }

源码 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)
    }
  }

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

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

缓存级别

<think>我们被要求回答关于RDD缓存方法的问题。根据引用内容,我们可以总结如下: 1. RDD缓存通过`persist()`或`cache()`方法实现。 2. 缓存不会立即执行,而是在触发action操作时才缓存。 3. `cache()`方法实际上是调用了`persist()`方法,默认存储级别为内存存储一份。 4. Spark提供了多种存储级别,定义在`StorageLevel`对象中。 5. 缓存是临时存储,用于提升效率,而checkpoint是持久化存储(如HDFS)用于容错。 此外,引用[5]展示了一个使用缓存方法的代码示例,包括`cache()`和`persist()`的不同使用方式。 因此,我们可以这样回答: 在Spark中,RDD缓存有两种主要方法:`persist()`和`cache()`。它们的作用是将RDD的计算结果保存起来,以便在后续操作中重用,从而避免重复计算,提高程序效率。 - **cache()方法**:是`persist()`方法的一个特例。它调用`persist(StorageLevel.MEMORY_ONLY)`,即将数据以非序列化的形式存储在JVM堆内存中。如果内存不足,部分分区将不会被缓存,并在需要时重新计算。 - **persist()方法**:可以指定不同的存储级别。默认情况下,`persist()`使用`StorageLevel.MEMORY_ONLY`。但用户可以根据需要选择其他存储级别,例如: - `MEMORY_AND_DISK`:优先内存,内存不足时溢写到磁盘 - `MEMORY_ONLY_SER`:内存中存储序列化后的对象(节省空间但增加CPU开销) - `DISK_ONLY`:只存储在磁盘上 等等(存储级别定义在`org.apache.spark.storage.StorageLevel`中)。 使用缓存时需要注意: - 缓存是惰性的,只有在触发action操作时才会真正缓存。 near - 缓存是临时存储,可能会因内存不足而被移除(根据LRU策略),或者程序结束就会释放。 - 对于需要多次使用的RDD,建议使用缓存,特别是当RDD的生成需要经过复杂计算或者需要多次触发action操作时。 代码示例(Scala): ```scala import org.apache.spark.storage.StorageLevel val rdd = sc.parallelize(1 to 100) // 使用cache方法 rdd.cache() // 使用persist方法,指定存储级别 rdd.persist(StorageLevel.MEMORY_AND_DISK_SER) ``` 在缓存RDD后,可以通过`unpersist()`方法来手动释放缓存。 另外,请注意缓存和检查点(checkpoint)的区别:检查点是将RDD数据持久化到可靠的分布式文件系统(如HDFS),因此数据更安全,但开销更大。 根据引用[4],在多次使用同一个RDD时,使用缓存可以避免重复计算,特别是避免重复的shuffle操作,从而提升性能。 §§相关问题§§ 1. RDD的存储级别有哪些?如何选择合适的存储级别? 2. 如何手动清除RDD缓存? 3. RDD缓存与检查点(checkpoint)的主要区别是什么? 4. 在什么场景下应该使用RDD缓存? 5. 如何监控RDD缓存的使用情况(如缓存大小、命中率等)? </think> ### RDD缓存方法详解 在Spark中,RDD缓存通过两种核心方法实现: 1. **`cache()`方法** 这是最常用的缓存方法,本质上是`persist()`的简化版。调用`cache()`会将RDD以**非序列化对象**形式存储在JVM堆内存中(存储级别为`MEMORY_ONLY`)。示例: ```scala val rdd = sc.parallelize(1 to 1000000) rdd.cache() // 标记为待缓存 ``` 2. **`persist()`方法** 提供更灵活的存储级别控制,支持多种持久化策略。默认行为与`cache()`相同,但可通过参数指定不同存储级别: ```scala rdd.persist() // 默认等价于cache() rdd.persist(StorageLevel.MEMORY_AND_DISK_SER) // 序列化存储,内存不足时溢写磁盘 ``` #### 关键特性 - **惰性执行**:调用缓存方法不会立即生效,只有在触发**action操作**(如`count()`、`collect()`)时才会真正缓存数据[^1][^3] - **存储级别**:通过`StorageLevel`指定,常用选项包括: - `MEMORY_ONLY`(默认):仅内存存储,反序列化对象 - `MEMORY_AND_DISK`:内存不足时溢写到磁盘 - `MEMORY_ONLY_SER`:序列化存储(节省空间但增加CPU开销) - `DISK_ONLY`:仅磁盘存储 - 带`_2`后缀的级别(如`MEMORY_AND_DISK_2`)会创建副本提高容错性[^5] - **重用机制**:缓存后,后续所有依赖该RDD的操作直接读取缓存,避免重复计算[^4] #### 使用场景与优势 1. **迭代算法**:机器学习中多次迭代使用同一数据集时 2. **交互式查询**:需要反复访问中间结果集的场景 3. **性能优化**:避免重复执行昂贵操作(如shuffle),尤其当RDD被多次使用时[^4] ```scala // 未缓存:执行4次shuffle val data = sc.textFile("huge_file.txt") val ipCounts = data.map(extractIP).reduceByKey(_+_) ipCounts.sortBy(_._2, false).take(10) // 触发shuffle ipCounts.sortBy(_._2, true).take(10) // 再次触发shuffle // 缓存后:仅1次shuffle ipCounts.cache() ipCounts.sortBy(...) // 首次action,缓存数据 ipCounts.sortBy(...) // 直接使用缓存 ``` #### 注意事项 - **内存管理**:缓存数据可能被LRU策略淘汰,带`_DISK`级别可避免重算 - **容错差异**:缓存是临时存储,节点故障导致数据丢失需重算;检查点(checkpoint)持久化到HDFS提供强容错[^2] - **释放缓存**:通过`unpersist()`手动释放资源 ```scala val cachedRDD = rdd.cache() cachedRDD.count() // 触发缓存 ... // 使用缓存数据 cachedRDD.unpersist() // 释放内存 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值