RDD的缓存
作用
- 通过缓存提升RDD的计算效率,一般当RDD的计算非常耗时,计算规则复杂,或者结果需要被重复使用时,可以将RDD的计算结果缓存起来,便于后续的使用
- 通过缓存提升RDD的容错能力,当后续计算失败后,尽量不让RDD回溯所有的依赖链条,从而减少计算时间
注意
- 缓存仅仅是一种临时的存储,可能会存在数据丢失, 所以缓存操作, 并不会将RDD之间的依赖关系给截断掉(丢失掉),因为当缓存失效后, 可以基于原有依赖关系重新计算
- 缓存数据可以保存到内存(executor内存空间),也可以保存到磁盘中, 甚至支持将缓存数据保存到堆外内存中(executor以外的系统空间)
- 缓存的API都是惰性的, 如果需要立即触发缓存操作, 必须后续跟上一个action算子, 一般建议使用count,如果不添加action算子, 只有当后续遇到第一个action算子后, 才会触发缓存
使用
默认情况下, 当整个Spark应用程序执行完成后, 缓存也会自动失效的, 自动删除
常用的缓存级别:
MEMORY_ONLY
: 仅缓存到内存中DISK_ONLY
: 仅缓存到磁盘MEMORY_AND_DISK
: 内存 + 磁盘 优先缓存到内存中, 当内存不足的时候, 剩余数据缓存到磁盘OFF_HEAP
: 缓存到堆外内存
最为常用的: MEMORY_AND_DISK
# 执行缓存操作 仅能将数据缓存到内存中
rdd.cache()
# 执行缓存操作, 默认将数据缓存到内存中, 当然也可以自定义缓存位置
rdd.persist(storageLevel=缓存的级别)
# 手动清理缓存的API
rdd.unpersist()
代码演示
import os
from pyspark import SparkContext, SparkConf, StorageLevel
# 锁定远端环境, 确保环境统一
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 xuqiu1():
rdd_res = rdd_map.map(lambda field_tuple: (field_tuple[2])) \
.map(lambda keyword: (keyword, 1)) \
.reduceByKey(lambda agg, curr: agg + curr) \
.top(10, lambda res_tup: res_tup[1])
print(rdd_res)
def xuqiu2():
rdd_res = rdd_map.map(lambda field_tup: ((field_tup[1], field_tup[2]), 1)) \
.reduceByKey(lambda agg, curr: agg + curr) \
.top(10, lambda res_tup: res_tup[1])
print(rdd_res)
if __name__ == '__main__':
"""
清洗需求:
需要先对数据进行清洗转换处理操作, 清洗掉为空的数据,
以及数据字段个数不足6个的数据, 并且将每一行的数据放置到一个元组中,
元组中每一个元素就是一个字段的数据
"""
print("Spark的Python模板")
# 创建核心
conf = SparkConf().setAppName('测试')
sc = SparkContext(conf=conf)
# 读取外部文件
rdd = sc.textFile(name='file:///export/data/pyprojects/pyspark/data/SogouQ.sample')
# 执行清洗需求相关操作
rdd_filter = rdd.filter(lambda line: len(line.split()) == 6)
rdd_map = rdd_filter.map(lambda line: (
line.split()[0], line.split()[1], line.split()[2][1:-1], line.split()[3], line.split()[4], line.split()[5]))
# 3.1 由于后续rdd_map被多次使用,此时可以将其设置为缓存,使用count动作算子,让缓存操作立即执行
rdd_map.persist(storageLevel=StorageLevel.MEMORY_AND_DISK).count()
# 需求一: 统计每个关键词出现了多少次, 获取前10个
xuqiu1()
# 3.2 当需求1执行完成, 让缓存失效
rdd_map.unpersist().count()
# 需求二:统计每个用户每个搜索词点击的次数
xuqiu2()
查看DAG图验证缓存是否被使用
代码中步骤3.1设置缓存后,需求1的任务DAG图中,有一个绿色的点,表示缓存点
代码中步骤3.2让缓存失效后,需求2的任务DAG图中,绿色的缓存点就消失了
RDD的checkpoint检查点
作用
- 提高RDD容错性,基于HDFS存储数据,确保RDD的数据不会丢失
- 提高RDD的执行效率,但是由于是基于HDFS存储,性能不如缓存
注意
- checkpoint类似于缓存操作,区别在于缓存把数据保存在内存或磁盘中,checkpoint将数据保存在HDFS或磁盘中
- checkpoint主要是基于HDFS存储数据,提供了更案例可靠的持久化方案,确保RDD的数据不会发生丢失,所以构建checkpoint后,会将RDD之间的依赖关系截断,后续计算出错时,直接从检查点恢复数据
使用
# 第一步: 设置检查点保存数据位置
sc.setCheckpointDir('路径地址')
# 第二步: 在对应RDD开启检查点
rdd.checkpoint()
rdd.count()
# 注意:
如果运行在集群模式中, checkpoint的保存的路径地址必须是HDFS
如果是local模式 可以支持在本地路径
checkpoint数据不会自动删除, 必须同时手动方式将其删除掉
代码演示
import time
from pyspark import SparkContext, SparkConf
from pyspark.sql import SparkSession
import os
# 锁定远端环境, 确保环境统一
os.environ['SPARK_HOME'] = '/export/server/spark'
os.environ['PYSPARK_PYTHON'] = '/root/anaconda3/bin/python3'
os.environ['PYSPARK_DRIVER_PYTHON'] = '/root/anaconda3/bin/python3'
if __name__ == '__main__':
print("演示checkpoint相关的操作")
# 1- 创建SparkContext对象
conf = SparkConf().setAppName('checkpoint').setMaster('local[*]')
sc = SparkContext(conf=conf)
# 开启检查点, 设置检查点的路径
sc.setCheckpointDir('/spark/chk') # 默认的地址为HDFS
# 2- 获取数据集
rdd = sc.parallelize(['张三 李四 王五 赵六', '田七 周八 李九 老张 老王 老李'])
# 3- 执行相关的操作: 以下操作仅仅是为了让依赖链条更长, 并没有太多的实际意义
rdd1 = rdd.flatMap(lambda line: line.split())
rdd2 = rdd1.map(lambda name: (name, 1))
rdd3 = rdd2.map(lambda name_tuple: (f'{name_tuple[0]}_itcast', name_tuple[1]))
rdd3 = rdd3.repartition(3)
rdd4 = rdd3.map(lambda name_tuple: name_tuple[0])
# RDD4设置检查点:
rdd4.checkpoint()
rdd4.count()
rdd5 = rdd4.flatMap(lambda name: name.split('_'))
rdd5 = rdd5.repartition(4)
rdd6 = rdd5.map(lambda name: (name, 1))
rdd_res = rdd6.reduceByKey(lambda agg, curr: agg + curr)
print(rdd_res.collect())
查看DAG图验证checkpoint是否被使用
未使用checkpoint时
使用checkpoint时