《深入理解Spark RDD缓存机制》(第4天)

文章目录

  • 前言
  • 一、小试牛刀:解剖RDD缓存机制?
    • [1. 什么是Spark RDD缓存策略](#1. 什么是Spark RDD缓存策略)
      • [1.1 为什幺RDD要做缓存](#1.1 为什幺RDD要做缓存)
      • [1.2 缓存相关API:](#1.2 缓存相关API:)
      • [1.3 缓存案例解析:](#1.3 缓存案例解析:)
      • [1.4 图解缓存效果:](#1.4 图解缓存效果:)
    • [2. 什么是checkpoint缓存](#2. 什么是checkpoint缓存)
      • [2.1 为什么要做checkpoint缓存](#2.1 为什么要做checkpoint缓存)
      • [2.2 checkpoint相关API:](#2.2 checkpoint相关API:)
      • [2.3 checkpoint案例解析](#2.3 checkpoint案例解析)
    • [3. 缓存和checkpoint的区别](#3. 缓存和checkpoint的区别)
      • [3.1 案例解析](#3.1 案例解析)
  • 二、打铁趁热:面试题思考
    • [1. cache缓存和checkpoint检查点的区别](#1. cache缓存和checkpoint检查点的区别)
    • [2. 既然持久化的方案有两种,那么在生产环境中应该使用哪种方案呢?](#2. 既然持久化的方案有两种,那么在生产环境中应该使用哪种方案呢?)
  • 总结

前言

Apache Spark是一个大规模数据处理框架,它提供了高效、快速和通用的数据处理能力。在Spark中,弹性分布式数据集(RDD, Resilient Distributed Dataset)是一个核心概念,而RDD的缓存机制则是确保Spark性能高效的关键因素之一。本文将通过'案例','图文'等解析方式深入探讨Spark RDD的缓存机制。


一、小试牛刀:解剖RDD缓存机制?

1. 什么是Spark RDD缓存策略

  • 在Spark中,RDD的缓存机制允许我们将计算的结果存储在内存中,从而避免在后续的计算中重复计算相同的RDD。这对于迭代计算、机器学习等场景尤为重要,可以显著提高计算效率。

1.1 为什幺RDD要做缓存

  • 当RDD被重复使用,或者计算该RDD比较容易出错,而且需要消耗比较多的资源和时间的时候,我们就可以将该RDD缓存起来。
  • 主要作用: 提升Spark程序的计算效率
  • 注意事项: RDD的缓存可以存储在内存或者是磁盘上,甚至可以存储在Executor进程的堆外内存中。主要是放在内存中,因此缓存的数据是不太稳定可靠。
    由于是临时存储,可能会存在丢失,所以缓存操作,并不会将RDD之间的依赖关系给截断掉(丢失掉),因为当缓存失效后,可以全部重新计算且缓存的API都是Lazy惰性的,如果需要触发缓存操作,推荐调用"count算子" ,因为运行效率高

1.2 缓存相关API:

设置缓存的API: 
	rdd.cache(): 将RDD的数据缓存储内存中
	rdd.persist(缓存的级别/位置): 将RDD的数据存储在指定位置

手动清理缓存API:
	rdd.unpersist()
默认情况下,当整个Spark应用程序执行完成后,缓存数据会自动失效,会被自动删除


缓存的级别/位置:
    DISK_ONLY: 只存储在磁盘
    DISK_ONLY_2: 只存储在磁盘,并且有2个副本
    DISK_ONLY_3: 只存储在磁盘,并且有3个副本
    MEMORY_ONLY: 只存储在内存中
    MEMORY_ONLY_2: 只存储在内存中,并且有2个副本
    MEMORY_AND_DISK: 存储在内存和磁盘中,先放在内存,再放在磁盘
    MEMORY_AND_DISK_2: 存储在内存和磁盘中,先放在内存,再放在磁盘,并且有2个副本
    OFF_HEAP: Executor进程的堆外内存
    
工作中最常用的是: MEMORY_AND_DISK和MEMORY_AND_DISK_2

1.3 缓存案例解析:

# 导包
import os
import time

import jieba
from pyspark import SparkConf, SparkContext, StorageLevel

# 绑定指定的python解释器
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'


def get_topN_keyword(etlRDD, n):
    r1 = etlRDD.flatMap(lambda line_list: list(jieba.cut(line_list[2]))) \
        .filter(lambda word: word not in ('.', '+', '的')) \
        .map(lambda word: (word, 1)) \
        .reduceByKey(lambda agg, curr: agg + curr) \
        .top(n, lambda t: t[1])
    print(r1)


def get_topN_search(etlRDD, n):
    r2 = etlRDD.map(lambda line_list: ((line_list[1], line_list[2]), 1)) \
        .reduceByKey(lambda agg, curr: agg + curr) \
        .top(n, lambda t: t[1])
    print(r2)


# 创建main函数
if __name__ == '__main__':
    # 1.创建SparkContext对象
    conf = SparkConf().setAppName('pyspark_demo').setMaster('local[*]')
    sc = SparkContext(conf=conf)
    # 2.数据输入
    textRDD = sc.textFile('file:///export/data/spark_project/spark_core/data/SogouQ.sample')
    # 3.数据处理(切分,转换,分组聚合)
    etlRDD = textRDD.filter(lambda line: line.strip() != '').map(lambda line: line.split()).filter(
        lambda line_list: len(line_list) >= 6)
    # 去除搜索内容两端的 [ ]
    etlRDD = etlRDD.map(lambda line_list:
                        [
                            line_list[0],
                            line_list[1],
                            line_list[2][1:-1],
                            line_list[3],
                            line_list[4],
                            line_list[5]
                        ])
    # 不加缓存
    # etlRDD.count()
    # 7.TODO: cache添加缓存,注意: 只能把缓存添加内存!相对用的少
    # etlRDD.cache().count()
    # 8.TODO: persist添加缓存,注意: 可以修改缓存级别
    etlRDD.persist(storageLevel=StorageLevel.MEMORY_AND_DISK_2).count()

    # 4.数据输出
    # 需求一: 统计每个 关键词 出现了多少次, 最终展示top10数据 注意:'.', '+', '的'  都需要过滤
    # 伪SQL:select 关键词 ,count(*)  from  搜狗表 group by 关键词
    get_topN_keyword(etlRDD, 10)

    # 8.TODO: 如果不想用缓存,可以使用unpersist释放缓存,给哪个rdd加的,就给哪个释放
    etlRDD.unpersist()

    # 需求二: 统计每个用户 每个 搜索内容 点击的次数, 最终展示top5数据
    # 伪SQL:select 用户,搜索内容,count(*)  from  搜狗表 group by 用户,搜索内容
    get_topN_search(etlRDD, 5)
    # 6.为了方便查看页面,可以让程序多睡会儿
    time.sleep(500)
    # 5.关闭资源
    sc.stop()

1.4 图解缓存效果:

  • 无缓存的DAG流程图显示:

  • 有缓存的DAG流程图显示:

  • cache基于内存

  • persist可以修改缓存级别: 同时基于内存和磁盘

2. 什么是checkpoint缓存

Checkpoint缓存,或称Checkpoint机制,是Apache Spark中用于确保数据一致性和容错性的一种技术。在不同的系统中,其实现方式和用途略有不同,但核心思想是一致的:确保关键数据或中间计算结果被安全地存储,以便在系统崩溃或需要恢复时能够重新使用。

2.1 为什么要做checkpoint缓存

  • RDD缓存主要是将数据存储在内存中,是临时存储,不太稳定,它主要是用来提升程序运行效率的。RDD的checkpoint(检查点)主要是将数据存储在HDFS上,是持久化存储。而HDFS存储数据有3副本的机制,让数据更加安全可靠。

  • checkpoint认为使用磁盘或者HDFS存储数据之后,数据非常的安全可靠,因此checkpoint会将RDD间的依赖关系给删除/丢弃掉。因此如果checkpoint的数据真的出现了问题,是无法在从头开始计算。

  • checkpoint主要作用: 提高程序的容错性

    注意事项: checkpoint可以将数据存储在磁盘或者HDFS上,主要是将数据存储在HDFS上。

2.2 checkpoint相关API:

	sc.setCheckpointDir(存储路径): 设置checkpoint数据存放路径
	rdd.checkpoint(): 对指定RDD启用checkpoint
	rdd.count(): 触发checkpoint

2.3 checkpoint案例解析

# 导包
import os
import time

import jieba
from pyspark import SparkConf, SparkContext, StorageLevel

# 绑定指定的python解释器
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'


def get_topN_keyword(etlRDD, n):
    r1 = etlRDD.flatMap(lambda line_list: list(jieba.cut(line_list[2]))) \
        .filter(lambda word: word not in ('.', '+', '的')) \
        .map(lambda word: (word, 1)) \
        .reduceByKey(lambda agg, curr: agg + curr) \
        .top(n, lambda t: t[1])
    print(r1)


def get_topN_search(etlRDD, n):
    r2 = etlRDD.map(lambda line_list: ((line_list[1], line_list[2]), 1)) \
        .reduceByKey(lambda agg, curr: agg + curr) \
        .top(n, lambda t: t[1])
    print(r2)


# 创建main函数
if __name__ == '__main__':
    # 1.创建SparkContext对象
    conf = SparkConf().setAppName('pyspark_demo').setMaster('local[*]')
    sc = SparkContext(conf=conf)
    # 2.数据输入
    textRDD = sc.textFile('file:///export/data/spark_project/spark_core/data/SogouQ.sample')
    # 3.数据处理(切分,转换,分组聚合)
    etlRDD = textRDD.filter(lambda line: line.strip() != '').map(lambda line: line.split()).filter(
        lambda line_list: len(line_list) >= 6)
    # 去除搜索内容两端的 [ ]
    etlRDD = etlRDD.map(lambda line_list:
                        [
                            line_list[0],
                            line_list[1],
                            line_list[2][1:-1],
                            line_list[3],
                            line_list[4],
                            line_list[5]
                        ])
    # 不加缓存
    # etlRDD.count()
    # 7.TODO: 先拿着sc对象设置检查点保存位置, 建议用hdfs,这样能利用hdfs的高可靠高可用性
    sc.setCheckpointDir('hdfs://node1:8020/ckpt')
    # 8.TODO: 添加检查点checkpoint
    etlRDD.checkpoint()
    etlRDD.count()

    # 4.数据输出
    # 需求一: 统计每个 关键词 出现了多少次, 最终展示top10数据 注意:'.', '+', '的'  都需要过滤
    # 伪SQL:select 关键词 ,count(*)  from  搜狗表 group by 关键词
    get_topN_keyword(etlRDD, 10)

    # 需求二: 统计每个用户 每个 搜索内容 点击的次数, 最终展示top5数据
    # 伪SQL:select 用户,搜索内容,count(*)  from  搜狗表 group by 用户,搜索内容
    get_topN_search(etlRDD, 5)
    # 6.为了方便查看页面,可以让程序多睡会儿
    time.sleep(500)
    # 5.关闭资源
    sc.stop()
  • 没有设置检查点正常的DAG执行流图:
  • 设置检查点后:

3. 缓存和checkpoint的区别

  • 面试题:Spark提供了两种持久化方案。一种为缓存操作,一种为checkpoint方案。请问有什么区别呢?

    1- 数据存储位置不同
    缓存: 存储在内存或者磁盘 或者 堆外内存中
    checkpoint检查点: 可以将数据存储在磁盘或者HDFS上, 在集群模式下, 仅能保存到HDFS上

    2- 数据生命周期:
    缓存: 当程序执行完成后, 或者手动调用unpersist 缓存都会被删除
    checkpoint检查点: 即使程序退出后, checkpoint检查点的数据依然是存在的, 不会删除, 需要手动删除

    3- 血缘关系:
    缓存: 不会截断RDD之间的血缘关系, 因为缓存数据有可能是失效, 当失效后, 需要重新回溯计算操作
    checkpoint检查点: 会截断掉依赖关系, 因为checkpoint将数据保存到更加安全可靠的位置, 不会发生数据丢失的问题, 当执行失败的时候, 也不需要重新回溯执行

    4- 主要作用不同:
    缓存: 提高Spark程序的运行效率和容错性
    checkpoint检查点: 提高Spark程序的容错性和高可用,高可靠性

  • 思考:既然持久化的方案有两种,那么在生产环境中应该使用哪种方案呢?

    在同一个项目中,推荐缓存和checkpoint(检查点)同时配合使用。

    使用顺序如下: 在代码中设置缓存和checkpoint检查点,然后再一同使用Action算子触发!!!
    使用count算子触发

    实际过程如下: 程序会优先从缓存中读取数据,如果发现缓存中没有数据。
    再从checkpoint中读取数据,并且接着将读取到的数据重新在内存中放置一份,
    后续还是优先从缓存中读取

3.1 案例解析

# 导包
import os
import time

import jieba
from pyspark import SparkConf, SparkContext, StorageLevel

# 绑定指定的python解释器
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'


def get_topN_keyword(etlRDD, n):
    r1 = etlRDD.flatMap(lambda line_list: list(jieba.cut(line_list[2]))) \
        .filter(lambda word: word not in ('.', '+', '的')) \
        .map(lambda word: (word, 1)) \
        .reduceByKey(lambda agg, curr: agg + curr) \
        .top(n, lambda t: t[1])
    print(r1)


def get_topN_search(etlRDD, n):
    r2 = etlRDD.map(lambda line_list: ((line_list[1], line_list[2]), 1)) \
        .reduceByKey(lambda agg, curr: agg + curr) \
        .top(n, lambda t: t[1])
    print(r2)


# 创建main函数
if __name__ == '__main__':
    # 1.创建SparkContext对象
    conf = SparkConf().setAppName('pyspark_demo').setMaster('local[*]')
    sc = SparkContext(conf=conf)
    # 2.数据输入
    textRDD = sc.textFile('file:///export/data/spark_project/spark_core/data/SogouQ.sample')
    # 3.数据处理(切分,转换,分组聚合)
    etlRDD = textRDD.filter(lambda line: line.strip() != '').map(lambda line: line.split()).filter(
        lambda line_list: len(line_list) >= 6)
    # 去除搜索内容两端的 [ ]
    etlRDD = etlRDD.map(lambda line_list:
                        [
                            line_list[0],
                            line_list[1],
                            line_list[2][1:-1],
                            line_list[3],
                            line_list[4],
                            line_list[5]
                        ])
    # 不加缓存
    # etlRDD.count()
    # 7.TODO: persist添加缓存,注意: 可以修改缓存级别
    etlRDD.persist(storageLevel=StorageLevel.MEMORY_AND_DISK_2)
    # 8.TODO: 先拿着sc对象设置检查点保存位置, 建议用hdfs,这样能利用hdfs的高可靠高可用性
    sc.setCheckpointDir('hdfs://node1:8020/ckpt')
    etlRDD.checkpoint()
    etlRDD.count()
    # TODO:触发缓存和检查点
    etlRDD.count()

    # 4.数据输出
    # 需求一: 统计每个 关键词 出现了多少次, 最终展示top10数据 注意:'.', '+', '的'  都需要过滤
    # 伪SQL:select 关键词 ,count(*)  from  搜狗表 group by 关键词
    get_topN_keyword(etlRDD, 10)

    # 7.TODO: 如果不想用缓存,可以使用unpersist释放缓存,给哪个rdd加的,就给哪个释放
    # etlRDD.unpersist()

    # 需求二: 统计每个用户 每个 搜索内容 点击的次数, 最终展示top5数据
    # 伪SQL:select 用户,搜索内容,count(*)  from  搜狗表 group by 用户,搜索内容
    get_topN_search(etlRDD, 5)
    # 6.为了方便查看页面,可以让程序多睡会儿
    time.sleep(500)
    # 5.关闭资源
    sc.stop()
  • DAG有向无环图:

二、打铁趁热:面试题思考

1. cache缓存和checkpoint检查点的区别

1- 数据存储位置不同
	缓存: 存储在内存或者磁盘 或者 堆外内存中
	checkpoint检查点: 可以将数据存储在磁盘或者HDFS上, 在集群模式下, 仅能保存到HDFS上

2- 数据生命周期:
	缓存: 当程序执行完成后, 或者手动调用unpersist 缓存都会被删除
	checkpoint检查点: 即使程序退出后, checkpoint检查点的数据依然是存在的, 不会删除, 需要手动删除

3- 血缘关系:
	缓存: 不会截断RDD之间的血缘关系, 因为缓存数据有可能是失效, 当失效后, 需要重新回溯计算操作
	checkpoint检查点: 会截断掉依赖关系, 因为checkpoint将数据保存到更加安全可靠的位置, 不会发生数据丢失的问题, 当执行失败的时候, 也不需要重新回溯执行
	
4- 主要作用不同:
	缓存: 提高Spark程序的运行效率和容错性
	checkpoint检查点: 提高Spark程序的容错性和高可用,高可靠性

2. 既然持久化的方案有两种,那么在生产环境中应该使用哪种方案呢?

在同一个项目中,推荐缓存和checkpoint(检查点)同时配合使用。

使用顺序如下: 在代码中设置缓存和checkpoint检查点,然后再一同使用Action算子触发!!! 
使用count算子触发

实际过程如下: 程序会优先从缓存中读取数据,如果发现缓存中没有数据。
再从checkpoint中读取数据,并且接着将读取到的数据重新在内存中放置一份,
后续还是优先从缓存中读取

总结

本文主要通过案例和图文的方式详解了Spark RDD 数据持久化的2种方案,重点思考项目中该采取什么方案。

相关推荐
用户Taobaoapi20144 分钟前
淘宝商品列表查询 API 接口详解
大数据
涛思数据(TDengine)1 小时前
taosd 写入与查询场景下压缩解压及加密解密的 CPU 占用分析
大数据·数据库·时序数据库·tdengine
DuDuTalk1 小时前
DuDuTalk接入DeepSeek,重构企业沟通数字化新范式
大数据·人工智能
大数据追光猿1 小时前
Qwen 模型与 LlamaFactory 结合训练详细步骤教程
大数据·人工智能·深度学习·计算机视觉·语言模型
驜鸈1 小时前
Redis常见命令
数据库·redis·缓存
左灯右行的爱情2 小时前
Redis- 热key
数据库·redis·缓存
Elastic 中国社区官方博客2 小时前
使用 Elastic-Agent 或 Beats 将 Journald 中的 syslog 和 auth 日志导入 Elastic Stack
大数据·linux·服务器·elasticsearch·搜索引擎·信息可视化·debian
ʃknight2 小时前
redis
数据库·redis·缓存
对许3 小时前
Hadoop的运行模式
大数据·hadoop·分布式
&有梦想的咸鱼&3 小时前
Android OKHttp缓存模块原理分析
android·okhttp·缓存