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

文章目录

  • 前言
  • 一、小试牛刀:解剖RDD缓存机制?
    • 1. 什么是Spark RDD缓存策略
      • 1.1 为什幺RDD要做缓存
      • 1.2 缓存相关API:
      • 1.3 缓存案例解析:
      • 1.4 图解缓存效果:
    • 2. 什么是checkpoint缓存
      • 2.1 为什么要做checkpoint缓存
      • 2.2 checkpoint相关API:
      • 2.3 checkpoint案例解析
    • 3. 缓存和checkpoint的区别
      • 3.1 案例解析
  • 二、打铁趁热:面试题思考
    • 1. cache缓存和checkpoint检查点的区别
    • 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种方案,重点思考项目中该采取什么方案。

  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Spark RDD(弹性分布式数据集)的依赖机制Spark实现容错性和高效计算的核心机制之一。 在Spark中,每个RDD都表示一个可分区、只读的数据集。RDD之间的依赖关系描述了RDD之间的转换操作,并指示了RDD如何通过转换操作从父RDD生成新的子RDDRDD的依赖关系可以分为两种类型:窄依赖(Narrow Dependency)和宽依赖(Wide Dependency)。 1. 窄依赖:当一个父RDD的每个分区只被一个子RDD的一个分区所使用时,我们称之为窄依赖。在这种情况下,Spark可以高效地进行转换操作,而无需重新计算所有的数据。例如,map、filter等转换操作都属于窄依赖。窄依赖允许Spark在计算中进行更多的优化,如任务划分、数据本地性等。 2. 宽依赖:当一个父RDD的分区被多个子RDD的分区所使用时,我们称之为宽依赖。在这种情况下,Spark需要通过将数据进行洗牌(Shuffle)操作来重新组织数据,以满足子RDD的需求。例如,groupByKey、reduceByKey等转换操作都属于宽依赖。洗牌操作需要涉及数据的网络传输和排序,因此会引入额外的开销。 Spark使用DAG(有向无环图)来表示RDD之间的依赖关系。每个RDD都包含其对应的转换操作和所依赖的父RDD。当执行一个Action操作时,Spark会根据RDD之间的依赖关系动态构建执行计划,并将其转化为一系列的任务来执行。 通过依赖机制Spark可以实现容错性,即当某个分区的数据丢失或计算失败时,可以通过依赖关系重新计算该分区的数据。同时,Spark还可以根据依赖关系进行任务划分和数据本地化等优化,以提高计算效率和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值