spark-RDD原理

1.缓存和checkpoint机制

1.1缓存的使用

缓存级别

  • 指定缓存的数据位置

  • 默认是缓存到内存上

StorageLevel.DISK_ONLY # 将数据缓存到磁盘上

StorageLevel.DISK_ONLY_2 # 将数据缓存到磁盘上 保存两份

StorageLevel.DISK_ONLY_3 # 将数据缓存到磁盘上 保存三份

StorageLevel.MEMORY_ONLY # 将数据缓存到内存 默认

StorageLevel.MEMORY_ONLY_2 # 将数据缓存到内存 保存两份

StorageLevel.MEMORY_AND_DISK # 将数据缓存到内存和磁盘 优先将数据缓存到内存上,内存不足可以缓存到磁盘

StorageLevel.MEMORY_AND_DISK_2 = # 将数据缓存到内存和磁盘

StorageLevel.OFF_HEAP # 不使用 缓存在系统管理的内存上 heap jvm的java虚拟机中的heap

StorageLevel.MEMORY_AND_DISK_ESER # 将数据缓存到内存和磁盘 序列化操作,按照二进制存储,节省空间

使用

  • persist 使用该方法

  • cache 内部调用persist

  • 手动释放 unpersist

    缓存 实现持久化

    from pyspark import SparkContext
    from pyspark.storagelevel import StorageLevel
    sc = SparkContext()

    获取数据

    rdd = sc.parallelize([1, 2, 3, 4, 5])
    rdd_line = rdd.map(lambda x:x.split(','))

    将数据转为kv结构

    def func(x)
    return (x[2],int(x[3]))

    rdd_kv = rdd_line.map(func)

    进入reduce阶段

    先对kv数据进行分组

    rdd_groupby = rdd_kv.groupByKey()

    对分组后的结果进行缓存

    # storageLevel 修改缓存级别

    rdd_groupby.persist(storageLevel=StorageLevel.MEMORY_AND_DISK)

    触发缓存

    rdd_groupby.collect()

    获取kv数据中value部分数据

    rdd_count = rdd_groupby.mapValues(lambda x:len(list(x)))

    查看数据

    res_count = rdd_count.collect()
    print(res_count)

1.2checkpoint

也是将中间rdd数据存储起来,但是存储的位置实时分布式存储系统,可以进行永久保存,程序结束不会释放

复制代码
# checkpoint 持久化  将数据存储在hdfs上
from pyspark import SparkContext

# 创建对象
sc = SparkContext()

# 指定checkpoint存储的hdfs位置
sc.setCheckpointDir('hdfs://node1:8020/checkpoint')

# 生成rdd数据
rdd = sc.parallelize(['hadoop,spark','spark,python'])

# 字符串数据切割
rdd_split = rdd.map(lambda x:x.split(','))  # [[hadoop,spark],[spark,python]]

# 将二维列表转为一维
def func(x):
    return x

rdd_word = rdd_split.flatMap(func) # [hadoop,spark,spark,python]
# 持久化操作,可以使用缓存或checkpoint
# # 对rdd使用checkpoint
rdd_word.checkpoint()
# rdd_word.persist()
# # # 触发执行
print(rdd_word.getCheckpointFile())


# 将数据转为kv
rdd_kv1 = rdd_word.map(lambda x:(x,1))

rdd_kv2 = rdd_word.map(lambda x:(x,2))

rdd_kv3 = rdd_word.map(lambda x:(x,3))

rdd_kv4 = rdd_word.map(lambda x:(x,4))

rdd_kv5 = rdd_word.map(lambda x:(x,5))


# 查看kv数据
res = rdd_kv1.collect()
print(res)
res2 = rdd_kv2.collect()
print(res2)
res3 = rdd_kv3.collect()
print(res3)
res4 = rdd_kv4.collect()
print(res4)
res5 = rdd_kv5.collect()
print(res5)

2.数据共享

2.1广播变量

如果要在分布式计算里面分发大的变量数据,这个都会由Driver端进行分发,一般来讲,如果这个变量不是广播变量,那么每个task就会分发一份,这在task数目十分多的情况下Driver的带宽会成为系统的瓶颈,而且会大量消耗task服务器上的资源,如果将这个变量声明为广播变量,那么每个executor拥有一份,这个executor启动的task会共享这个变量,节省了通信的成本和服务器的资源。

减少task线程对应变量的定义,节省内存空间

复制代码
# 广播变量
from pyspark import SparkContext


sc  = SparkContext()

num = 10
# 将变量定义成广播变量
b_obj = sc.broadcast(num)

rdd = sc.parallelize([1,2,3,4])

# 转化计算
def func(x):
    # 广播变量无法修改
    # b_obj.value=20
    # 获取广播变量值
    return x+b_obj.value

rdd_map = rdd.map(func)

# 查看数据
res = rdd_map.collect()
print(res)

2.2累加器

避免资源抢占造成计算错误

复制代码
# 累加器
from pyspark import SparkContext


sc  = SparkContext()

num = 10
# 将变量定义成累加器
a_obj = sc.accumulator(num)
# 生成rdd
rdd = sc.parallelize([1,2,3,4])

# 对rdd进行计算
def func(x):
    print(x) # 输出rdd中元素数据
    # 对累加器的值进行修改 每次加1
    a_obj.add(1)
    return (x,1)

rdd_map = rdd.map(func)

# 查看数据
res = rdd_map.collect()
print(res)

# 查看累加器的数据
print(a_obj.value)

3.RDD的依赖关系

  • 窄依赖

    • 每个父RDD的一个Partition最多被子RDD的一个Partition所使用

      • map

      • flatMap

      • filter

  • 宽依赖

    • 一个父RDD的Partition会被多个子RDD的Partition所使用

      • groupbykey

      • reducebykey

      • sortBykey

    • 在宽依赖中rdd之间会发生数据交换,这个交换的过程称为rdd的shuffle

      • 只要是宽依赖必然发生shuffle

      • 在宽依赖进行数据交换时,只有等待所有分区数据交换完成后,才能进行后续的计算,非常影响计算速度

DAG 管理维护rdd之间依赖关系,保证代码的执行顺序。

4.shuffle过程

在 Spark 中,Shuffle 是一种将数据在不同的分区之间重新分布的过程,通常发生在一些特定的操作中,如 groupByKeyreduceByKeyjoin 等。Shuffle 过程涉及到数据的重新分区、排序和聚合等操作,对 Spark 作业的性能有很大的影响。

以下是 Spark Shuffle 的大致过程:

一、Mapper 阶段(Map 任务)

  1. 数据处理:

    • 每个输入分区的数据被分配到一个或多个 Mapper(Map 任务)进行处理。Mapper 会对输入数据进行转换操作,生成中间结果。
    • 例如,在 reduceByKey 操作中,Mapper 会将输入数据中的每个键值对进行处理,生成中间的键值对结果,其中键相同的值会被聚合在一起。
  2. 分区函数:

    • 根据指定的分区函数,将中间结果分配到不同的分区中。分区函数决定了每个键值对应该被分配到哪个分区。
    • 例如,在 HashPartitioner(默认的分区函数)中,根据键的哈希值来确定分区。如果有两个分区,键的哈希值对 2 取模,结果为 0 的键值对分配到一个分区,结果为 1 的键值对分配到另一个分区。
  3. 缓存中间结果:

    • Mapper 会将中间结果缓存在内存中,以便在后续的 Shuffle Write 阶段进行写入。如果内存不足,中间结果可能会被溢出到磁盘上。

二、Shuffle Write(混洗写阶段)

  1. 数据写入:

    • Mapper 任务将中间结果写入本地磁盘。每个 Mapper 会根据目标分区的数量,将数据写入多个文件,每个文件对应一个目标分区。
    • 这些文件通常是临时文件,包含了要发送到不同 Reducer 的数据。
  2. 索引文件:

    • 同时,Mapper 会为每个输出文件生成一个索引文件,记录了每个分区的数据在输出文件中的偏移量。索引文件用于在 Shuffle Read 阶段快速定位数据。

三、Shuffle Read(混洗读阶段)

  1. 数据读取:

    • Reducer(Reduce 任务)从各个 Mapper 的本地磁盘读取属于自己的分区数据。Reducer 会根据分区的索引文件,确定要读取哪些文件以及从文件中的哪个位置开始读取。
    • Reducer 会从多个 Mapper 读取数据,然后对相同键的数据进行聚合或其他操作。
  2. 数据合并:

    • Reducer 会将从不同 Mapper 读取的数据进行合并和排序。如果有多个 Mapper 输出了相同键的数据,Reducer 会将这些数据合并在一起,并按照键进行排序。
  3. 聚合操作:

    • 最后,Reducer 对合并后的数据进行聚合操作,生成最终的结果。聚合操作可以是求和、计数、求平均值等。

四、性能影响因素和优化

  1. 数据倾斜:

    • 如果某些键的值在数据集中出现的频率非常高,可能会导致数据倾斜。这意味着某些 Reducer 会处理大量的数据,而其他 Reducer 处理的数据量很少。数据倾斜会严重影响作业的性能,导致某些任务运行时间过长。
    • 解决数据倾斜的方法包括使用更合适的分区函数、对倾斜的键进行特殊处理(如随机前缀)、在 Mapper 阶段进行预聚合等。
  2. 内存管理:

    • Shuffle 过程中需要大量的内存来缓存中间结果和读取的数据。如果内存不足,可能会导致数据溢出到磁盘上,增加磁盘 I/O 开销,降低性能。
    • 可以通过调整 Spark 的内存参数(如 spark.executor.memoryspark.storage.memoryFraction 等)来优化内存使用。此外,也可以使用一些内存优化技术,如序列化、压缩等,减少内存占用。
  3. 网络传输:

    • Shuffle 过程中需要在不同的节点之间传输大量的数据。网络传输的性能会影响作业的整体执行时间。
    • 可以通过优化网络配置、使用高效的序列化格式(如 Kryo 序列化)、增加网络带宽等方式来提高网络传输性能。
  • park的shuffle的两个部分

    • shuffle wirte 写

    • shuffle read 读

    • 会进行文件的读写,影响spark的计算速度

  • spark的shuffle方法类

    • 是spark封装好的处理shuffle的方法

    • hashshuffle 类

      • 进行的是hash计算

      • spark1.2版本前主要使用,之后引入了sortshuffle

      • spark2.0之后,删除了hashshuffle ,从2.0版本开始使用sortshuffle类

      • 优化的hashshufulle和未优化

    • sortshuffle类

      • 排序方式将相同key值数据放在一起

      • sortshuffle类使用时,有两个方法实现shuffle

        • bypass模式版本和普通模式版本

        • bypass模式版本不会排序,会进行hash操作

        • 普通模式版本会排序进行shuffle

      • 可以通过配置指定按照那种模式执行 根据task数量决定 默认 task数量小于等于200 采用bypass,task数量超过200个则使用普通模式的方法进行shuffle

      • 一个分区对应一个task,所以task数量由分区数决定

相关推荐
goTsHgo27 分钟前
Flink的 RecordWriter 数据通道 详解
大数据·flink
Romantic Rose1 小时前
你所拨打的电话是空号?手机状态查询API
大数据·人工智能
随缘而动,随遇而安2 小时前
第四十六篇 人力资源管理数据仓库架构设计与高阶实践
大数据·数据库·数据仓库·sql·数据库架构
小宋10212 小时前
Linux安装Elasticsearch详细教程
大数据·elasticsearch·搜索引擎
程序员老周6663 小时前
数据仓库标准库模型架构相关概念浅讲
大数据·数据仓库·hive·数仓·拉链抽取·增量抽取·数据仓库架构
敏君宝爸3 小时前
kafka 配置SASL认证
分布式·kafka
斯普信云原生组3 小时前
kafka消费延迟
分布式·kafka
见未见过的风景3 小时前
使用 Redis + Redisson 分布式锁来生成全局唯一、线程安全的带日期前缀的流水号的完整实现。
数据库·redis·分布式
2301_787552874 小时前
aidigu开源微博项目程序,PHP开发的开源微博系统,自媒体个人创业、网盘推广首先
开发语言·开源·php·数据库开发·媒体
Made in Program5 小时前
从数据格式转换的角度 flink cdc 如何写入paimon?
大数据·flink·paimon