关于spark persist()的两个坑

本文深入探讨Spark中persist()和unpersist()操作的常见误区,包括persist()+show()仅缓存部分数据及unpersist()一个RDD时连带清除其子RDD的问题。通过实例展示如何正确使用缓存机制,避免计算重复和资源浪费。

目录

【坑1:persist() + show()不能缓存全部数据】

【坑2:unpersist()一个rdd时会同时unpersist()子RDD】


【坑1:persist() + show()不能缓存全部数据】

对于一个有100个分区的数据,假如你对其persist()后进行show(10),那么spark只会把第一个分区缓存起来,show前10条数据。如果你show的数据量超过了一个分区的大小,那么spark会多缓存一些分区。

因此persist()后如果希望数据全部都缓存到内存中,应对每个分区都执行action操作,如进行count()。

例子:

package high_quality._history

import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.SparkSession

object demo {

  def main(args: Array[String]) {

    Logger.getRootLogger.setLevel(Level.ERROR)
    val spark = SparkSession.builder().getOrCreate()
    import spark.implicits._

    val df = spark.sparkContext.parallelize(1 to 10000)
      .toDF("value")
      .repartition(100)
    df.persist()
    df.show(10)
    Thread.sleep(10000) // 在此时查看Spark UI的storage页面
    println(df.count())
    Thread.sleep(10000) // 在此时再次查看Spark UI的storage页面
  }
}

在第一次查看Storage时,可以见到这个DataFrame只有其中一个分区被缓存起来。而在第二次查看Storage时,由于执行了count()操作,100个分区全被缓存起来了。


【坑2:unpersist()一个rdd时会同时unpersist()子RDD】

在spark 2.4之前的版本,当你创建了一个DataFrame a,同时由a得到b,并且把2个DataFrame都缓存起来了,如果执行a.unpersist(),会把b也自动unpersist()。

这个潜在的坑会使得你的spark程序增加了大量的计算量,鄙人之前由于计算数据量大,把代码分成了八步,每一步结束后都对当前df进行了persist() + count(),然后对父rdd进行了unpersist,这样导致了第一步重复计算了8次、第二步重复计算了7次、...、第七步重复计算了2次,通过查看spark UI才发现了此问题。

例子如下:

package high_quality._history

import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.SparkSession

object demo {

  def main(args: Array[String]) {

    Logger.getRootLogger.setLevel(Level.ERROR)
    val spark = SparkSession.builder().getOrCreate()
    import spark.implicits._

    val a = Seq("1").toDF("x")
    val b = a.withColumn("y", $"x" + 1)
    a.persist()
    println(a.count())
    b.persist()
    println(b.count())
    Thread.sleep(10000) // 在此时查看Spark UI的Storage

    a.unpersist()
    Thread.sleep(10000) // 在此时再次查看Spark UI的Storage
  }
}

第一次查看spark UI,两个df都成功缓存:

第二次查看spark UI,可见当a.unpersist()后,b也被移出Storage了

解决方法是不进行unpersist(),或者内存不足时通过checkpoint()等方式把数据保存到hadoop或外部文件中。

<think> 我们正在处理用户关于Spark RDD写法示例的请求。根据提供的引用,我们可以总结以下几点关键信息: 1. 创建RDD:可以从外部数据源创建RDD(引用[1])。 2. RDD的特性:分布式、可分区、容错(引用[2])。 3. SparkSession作为统一入口(引用[3])。 注意:引用[4]的内容似乎包含无关甚至错误的信息(如将字符串转换为字符的方法),并且最后一句是无关的抒情,我们可以忽略。 因此,我们将重点给出RDD的创建、转换和行动操作的示例。 步骤: 1. 创建SparkSession(Spark 2.x以后)。 2. 创建RDD:可以从集合创建,也可以从外部存储系统(如HDFS、本地文件系统)创建。 3. 对RDD进行转换操作(transformation),如map、filter等。 4. 可以选择对需要重用的RDD进行持久化(persist)。 5. 触发行动操作(action),如count、collect等。 示例代码将包括: - 创建RDD的两种方式 - 转换操作示例(map, filter) - 持久化示例 - 行动操作示例 注意:由于用户要求的是RDD写法示例,我们将使用Scala语言(因为Spark原生支持Scala,且RDD API在Scala中最为常用),但也可以提供Python版本(PySpark)的示例。根据上下文,用户没有指定语言,但考虑到引用[1]中的示例代码是Python(快速排序),而Spark支持多种语言,我们可以提供两种语言的示例。 但注意用户问题中引用的代码示例是Python,因此我们优先提供Python(PySpark)的示例。 另外,引用[3]提到SparkSession,所以我们使用SparkSession来创建。 我们将按照以下结构编写: 1. 初始化SparkSession 2. 创建RDD 3. 转换操作 4. 持久化 5. 行动操作 6. 关闭SparkSession 由于用户要求获取写法示例,我们将提供完整的代码片段。 注意:在实际编写时,要确保代码的正确性。 我们开始编写示例: </think> 以下是Spark RDD的写法示例及核心概念解析: ### Spark RDD编程模型 RDD(弹性分布式数据集)是Spark的核心抽象,编程流程遵循:**创建RDD → 转换操作 → 持久化优化 → 行动操作** [^1][^2] #### 示例代码(Python/PySpark) ```python from pyspark.sql import SparkSession # 1. 创建SparkSession(统一入口)[^3] spark = SparkSession.builder.appName("RDDExample").getOrCreate() # 2. 创建RDD(从集合/外部数据源)[^1] data = [1, 2, 3, 4, 5] rdd = spark.sparkContext.parallelize(data) # 从集合创建 # text_rdd = spark.sparkContext.textFile("hdfs://path/to/file.txt") # 从文件创建 # 3. 转换操作(惰性求值) squared_rdd = rdd.map(lambda x: x*x) # 每个元素平方 filtered_rdd = squared_rdd.filter(lambda x: x > 5) # 过滤结果 # 4. 持久化优化(避免重复计算)[^1] filtered_rdd.persist() # 5. 行动操作(触发计算) print("元素数量:", filtered_rdd.count()) # 计数操作 print("首元素:", filtered_rdd.first()) # 获取第一个元素 # 6. 结果收集(本地调试用) print(filtered_rdd.collect()) # 输出: [9, 16, 25] # 关闭会话 spark.stop() ``` #### 关键概念说明 1. **创建RDD** - 从内存集合:`sparkContext.parallelize(data)` - 从外部存储:`sparkContext.textFile()`, `hadoopFile()` 等 [^1] 2. **转换操作(Transformation)** - `map(func)`:对每个元素执行函数,如 `lambda x: x*2` - `filter(func)`:过滤满足条件的元素 - `flatMap(func)`:先映射后扁平化(如分词处理) - `groupByKey()`:对键值对RDD按key分组 3. **持久化(Persistence)** - 调用 `.persist()` 缓存重复使用的中间结果 [^1] - 存储级别:`MEMORY_ONLY`, `MEMORY_AND_DISK` 等 4. **行动操作(Action)** - `count()`:返回元素总数 - `collect()`:以数组形式返回所有数据(**谨慎使用**) - `saveAsTextFile(path)`:保存到分布式存储 5. **容错机制** - 通过RDD血缘关系(Lineage DAG)重建丢失分区 [^2] - 所有转换记录形成谱系图,无需数据复制
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值