Spark缓存

**(一 ****)******日常生活中的类比:

  1. 厨房调料架:常用调料的"临时存放处"

场景:炒菜时,常用的盐、酱油、食用油会放在灶台边的调料架上(伸手就能拿到),而不常用的八角、桂皮等调料则收在橱柜里。

缓存逻辑:高频使用的调料("数据")存放在"高速访问区"(调料架),避免每次都打开橱柜("低速存储")翻找,提升做饭效率。

  1. 课本笔记:重点内容的"手抄小本本"

场景:复习时,会把课本里的重点公式、易错点抄在笔记本上("缓存区"),而不是每次都翻厚重的课本。

缓存逻辑:把高频查阅的信息("数据")提前存储在"快速访问载体"(笔记本)中,需要时直接看笔记("命中缓存"),省去翻书时间("从低速存储读取")。

  1. 手机APP的"离线缓存":提前下载的"临时资源包"

场景:视频APP会把你常看的电视剧前几集提前下载到手机存储("缓存"),即使断网也能快速播放;地图APP会缓存你常去的区域地图,避免重复加载。

缓存逻辑:预判你可能重复使用的内容("数据"),提前存放在"本地高速存储"(手机内存),减少网络加载("低速远程获取")的等待时间。

RDD缓存

前面介绍的是一般意义上的缓存,下面我们具体来介绍RDD缓存。

【教师讲授】

Spark速度非常快的原因之一,就是在不同操作中可以在内存中持久化或缓存多个数据集。当持久化某个RDD后,每一个节点都将把计算的分片结果保存在内存中,并在对此RDD或衍生出的RDD进行的其他动作中重用。这使得后续的动作变得更加迅速。RDD相关的持久化和缓存,是Spark最重要的特征之一。

RDD通过persist方法或cache方法可以将前面的计算结果缓存,但是并不是这两个方法被调用时立即缓存,而是触发后面的行动算子时,该RDD将会被缓存在计算节点的内存中,并供以后重用。

不带缓存的计算示例

理论介绍完了,下面我们来看看例子,来对比观察使用缓存和不用缓存的效率区别。

以下通过一个计算斐波那契数列(1,1,2,3,5,8,13,....)第n项的例子,来展示 Spark 中cache方法的运行效果。

先来看看一段代码。

import org.apache.spark.storage.StorageLevel

import org.apache.spark.{SparkConf, SparkContext}

object Cache {

def main(args: Array[String]): Unit = {

// 配置Spark

val conf = new SparkConf().setAppName("CacheExample").setMaster("local[*]")

val sc = new SparkContext(conf)

// conf.set("spark.local.dir", "_cache")

sc.setLogLevel("WARN")

// 创建一个包含大量随机数的 RDD

val largeRDD = sc.parallelize(1 to 1000*1000*10).map(_ => scala.util.Random.nextInt(1000))

// 定义一个复杂的转换函数

def complexTransformation(num: Int): Int = {

var result = num

for (_ <- 1 to 1000) {

result = result * 2 % 1000

}

result

}

// 不使用 cache 的情况

val nonCachedRDD = largeRDD.map(complexTransformation)

// 第一次触发行动算子,计算并统计时间

val startTime1 = System.currentTimeMillis()

val result1 = nonCachedRDD.collect()

val endTime1 = System.currentTimeMillis()

println(s"不使用 cache 第一次计算耗时: ${endTime1 - startTime1} 毫秒")

// 第二次触发行动算子,计算并统计时间

val startTime2 = System.currentTimeMillis()

val result2 = nonCachedRDD.collect()

val endTime2 = System.currentTimeMillis()

println(s"不使用 cache 第二次计算耗时: ${endTime2 - startTime2} 毫秒")

sc.stop()

}

}

核心代码说明:

1.map算子是转换算子,并不会导致真正的计算

2.第一次调用collect和第二调用collect花的时间基本一致。这就是没有缓存的效果。

带缓存的计算示例-cache

【教师讲解cache的格式,并直接添加进来】,下面我们引入spark的cache方法。

在 Scala 里,cache 方法定义于 org.apache.spark.rdd.RDD 类中,其方法签名如下:

scala

def cache(): this.type

返回类型:this.type,这表明返回的是调用该方法的 RDD 自身,只不过这个 RDD 已经被标记为需要缓存。

参数:此方法没有参数。

// 使用 cache 的情况

val cachedRDD = largeRDD.map(complexTransformation).cache()

// 第一次触发行动算子,计算并统计时间

val startTime3 = System.currentTimeMillis()

val result3 = cachedRDD.collect()

val endTime3 = System.currentTimeMillis()

println(s"使用 cache 第一次计算耗时: ${endTime3 - startTime3} 毫秒")

// 第二次触发行动算子,计算并统计时间

val startTime4 = System.currentTimeMillis()

val result4 = cachedRDD.collect()

val endTime4 = System.currentTimeMillis()

println(s"使用 cache 第二次计算耗时: ${endTime4 - startTime4} 毫秒")

println(s"spark.local.dir 的值: ${conf.get("spark.local.dir")}")

sc.stop()

核心代码说明:

第一次调用collect时,程序需要对RDD中的每个元素执行fibonacci函数进行计算,这涉及到递归运算,比较耗时。

第二次调用collect时,因为之前已经调用了cache方法,并且结果已被缓存,所以不需要再次执行计算,直接从缓存中读取数据。通过对比两次计算的耗时,可以明显发现第二次计算耗时会远小于第一次(在数据量较大或计算复杂时效果更显著),这就体现了cache方法缓存计算结果、避免重复计算、提升后续操作速度的作用 。

persist和cache方法

在 Spark 中,persist 和 cache 方法都用于将 RDD(弹性分布式数据集)或 DataFrame 持久化,以避免重复计算从而提升性能,但二者存在一些区别。

  1. 功能本质

persist:这是一个通用的持久化 方法,能够指定多种不同的存储级别。存储级别决定了数据的存储位置(如内存、磁盘)以及存储形式(如是否序列化)。

cache:其实是 persist 方法的一种特殊情况,它等价于调用 persist(StorageLevel.MEMORY_ONLY),也就是将数据以非序列化的 Java 对象形式存储在内存中。

  1. 存储级别指定

persist:可以通过传入 StorageLevel 参数来指定不同的持久化级别。常见的持久化级别有:

MEMORY_ONLY:将 RDD 以 Java 对象的形式存储在 JVM 的内存中。若内存不足,部分分区将不会被缓存,需要时会重新计算。

MEMORY_AND_DISK:优先把 RDD 以 Java 对象的形式存储在 JVM 的内存中。若内存不足,会把多余的分区存储到磁盘上。

DISK_ONLY:将 RDD 的数据存储在磁盘上。

MEMORY_ONLY_SER:将 RDD 以序列化的 Java 对象形式存储在内存中,相较于 MEMORY_ONLY,序列化后占用的内存空间更小,但读取时需要进行反序列化操作,会带来一定的性能开销。

MEMORY_AND_DISK_SER:优先将 RDD 以序列化的 Java 对象形式存储在内存中,内存不足时存储到磁盘上。

cache:不能指定存储级别,它固定使用 MEMORY_ONLY 存储级别。

persist的示例

下面我们以DISK_ONLY为例,改写上面的程序,验证它的持久化效果。具体要改动的地方有两个: 指定持久化地址; 把cache改成persist;

conf.set("spark.local.dir", "/path/to/your/local/dir")

sc = SparkContext(conf)

val cachedRDD = largeRDD.map(complexTransformation).persist(StorageLevel.MEMORY_ONLY)

相关推荐
Code哈哈笑2 分钟前
【图书管理系统】用户注册系统实现详解
数据库·spring boot·后端·mybatis
2401_837088504 分钟前
SQL性能分析
数据库·sql
瓜皮弟子头很铁4 分钟前
多项目共用SQL 添加需要字段
数据库·sql
CryptoRzz26 分钟前
股票数据源对接技术指南:印度尼西亚、印度、韩国
数据库·python·金融·数据分析·区块链
Pluto_CSND35 分钟前
hbase shell的常用命令
大数据·数据库·hbase
API_technology1 小时前
阿里巴巴 1688 数据接口开发指南:构建自动化商品详情采集系统
大数据·运维·数据挖掘·自动化
哈哈真棒1 小时前
sparkSQL读入csv文件写入mysql(2)
数据库·mysql
Cynicism_Smile1 小时前
Mysql 8.0.32 union all 创建视图后中文模糊查询失效
数据库·mysql
小oo呆1 小时前
【自然语言处理与大模型】向量数据库技术
数据库·人工智能·自然语言处理
Aurora_NeAr2 小时前
Redis设计与实现——Redis命令参考与高级特性
数据库·redis·缓存