当前位置: 首页 > news >正文

Spark的宽窄依赖

在 PySpark 中,RDD(弹性分布式数据集)之间的依赖关系是理解 Spark 计算模型的核心概念之一。根据依赖的特性,RDD 的依赖被分为窄依赖(Narrow Dependency) 和宽依赖(Wide Dependency,又称 Shuffle Dependency)。两者的核心区别在于子 RDD 分区对父 RDD 分区的依赖范围,以及是否会触发数据洗牌(Shuffle),这直接影响 Spark 的性能、容错和任务调度。

一、RDD 依赖的基本概念

RDD 是只读的分布式数据集,每个 RDD 都通过转换操作(如mapgroupByKey)从一个或多个父 RDD 衍生而来。这种 “子 RDD 依赖父 RDD” 的关系称为RDD 依赖。依赖关系决定了:

  • 如何计算子 RDD 的分区数据;
  • 数据是否需要在节点间传输(Shuffle);
  • 任务的调度方式和容错策略。

二、窄依赖(Narrow Dependency)

定义

窄依赖是指子 RDD 的每个分区仅依赖于父 RDD 的少数(通常是 1 个)分区

特点
  1. 无 Shuffle:子 RDD 的分区数据可以直接从父 RDD 的对应分区计算得到,无需跨节点传输数据,因此不会触发 Shuffle。
  2. 高效计算:计算可以在单个节点内完成(因为数据无需移动),资源开销小。
  3. 容错友好:若子 RDD 的某个分区丢失,只需重新计算父 RDD 中对应的少数分区即可恢复,效率高。
典型操作
  • map(f):父 RDD 的每个分区通过函数f转换为子 RDD 的一个分区(1 对 1 依赖)。
  • filter(f):父 RDD 的每个分区过滤后生成子 RDD 的一个分区(1 对 1 依赖)。
  • union(other):多个父 RDD 的分区直接合并为子 RDD 的分区(每个父 RDD 的分区对应子 RDD 的一个分区,多对多但范围明确)。
  • mapPartitions(f):对父 RDD 的每个分区应用函数f,生成子 RDD 的一个分区(1 对 1 依赖)。
  • flatMap(f):类似map,但输出是迭代器,仍为 1 对 1 依赖。
  • coalesce(n)(减少分区数时):将多个父分区合并为 fewer 子分区,无需跨节点(窄依赖)。
  • join(特殊情况):若两个 RDD 按相同 key 分区且使用相同分区器,子 RDD 分区仅依赖父 RDD 对应分区(1 对 1 依赖)。
示例
from pyspark import SparkContextsc = SparkContext("local", "NarrowDependencyExample")
rdd1 = sc.parallelize([1, 2, 3, 4, 5], numSlices=2)  # 2个分区:[1,2]、[3,4,5]
rdd2 = rdd1.map(lambda x: x * 2)  # map操作是窄依赖
# rdd2的分区依赖:分区0依赖rdd1的分区0,分区1依赖rdd1的分区1

三、宽依赖(Wide Dependency / Shuffle Dependency)

定义

宽依赖是指子 RDD 的每个分区依赖于父 RDD 的多个甚至所有分区

特点
  1. 触发 Shuffle:子 RDD 的分区需要聚合父 RDD 多个分区的数据,因此必须跨节点传输数据(Shuffle),开销大。
  2. 性能影响:Shuffle 涉及磁盘 IO 和网络传输,是 Spark 性能瓶颈的主要来源。
  3. 容错成本高:若子 RDD 的某个分区丢失,需重新计算父 RDD 的多个分区才能恢复,效率低。
典型操作
  • groupByKey():父 RDD 所有分区中相同 key 的数据需汇总到子 RDD 的同一分区(多对 1 依赖)。
  • reduceByKey(f):按 key 聚合,需收集父 RDD 多个分区的相同 key 数据(多对 1 依赖)。
  • sortByKey():按 key 排序,需全局重排,依赖父 RDD 所有分区。
  • distinct():去重需全局比较,依赖父 RDD 所有分区。
  • repartition(n):重新分区(无论增减),需 Shuffle(多对多依赖)。
  • join(一般情况):若两个 RDD 分区方式不同,需 Shuffle 后再 join(多对多依赖)。
示例
rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)], numSlices=2)
rdd_grouped = rdd.groupByKey()  # groupByKey是宽依赖
# rdd_grouped的分区0可能依赖rdd的分区0和1中key为"a"的数据,分区1依赖rdd的分区0和1中key为"b"的数据

四、宽窄依赖的核心区别

维度窄依赖宽依赖
依赖范围子 RDD 分区依赖父 RDD 的少数(1 个或几个)分区子 RDD 分区依赖父 RDD 的多个甚至所有分区
是否 Shuffle
性能开销低(本地计算)高(跨节点传输)
容错效率高(仅需重算少数父分区)低(需重算多个父分区)
Stage 划分影响同一 Stage 内可合并是 Stage 划分的边界(每个宽依赖开启新 Stage)
典型操作mapfilterunion(无 Shuffle 的joingroupByKeyrepartitionsortByKey

五、区分宽窄依赖的意义

  1. Stage 划分:Spark 的 DAG(有向无环图)中,Stage 的边界由宽依赖决定。每个宽依赖会开启一个新的 Stage,同一 Stage 内的操作均为窄依赖,可合并执行以减少开销。

  2. 任务调度:窄依赖操作可在单个节点内并行执行,而宽依赖需等待前一 Stage 完成后才能开始(因依赖 Shuffle 结果)。

  3. 性能优化:实际开发中应尽量避免不必要的宽依赖(如用reduceByKey替代groupByKey,减少 Shuffle 数据量),或通过预分区(如partitionBy)将宽依赖转为窄依赖(如join前确保两 RDD 分区一致)。

总结

宽窄依赖的本质是子 RDD 对父 RDD 分区的依赖范围差异,其核心影响是是否触发 Shuffle。理解这一概念有助于优化 Spark 作业性能(减少 Shuffle)、理解任务调度逻辑(Stage 划分)和容错机制。实际开发中,应优先使用窄依赖操作,并通过合理分区策略减少宽依赖的开销。

http://www.xdnf.cn/news/1215703.html

相关文章:

  • 第七章:进入Redis的SET核心
  • 重生之我在暑假学习微服务第五天《Docker部署项目篇》
  • 【人工智能99问】混合专家模型(MoE)是如何训练的?(18/99)
  • pytorch小记(三十三):PyTorch 使用 TensorBoard 可视化训练过程(含完整示例)
  • LLM—— 基于 MCP 协议(SSE 模式)的工具调用实践
  • 30道JS高频经典笔试题集合+详解(一)
  • 华为昇腾×绿算全闪存缓存释放澎湃潜能
  • 【LeetCode】链表反转实现与测试
  • 比特币挖矿的能源消耗和环保问题
  • 关于“LoggerFactory is not a Logback LoggerContext but Logback is on ......“的解决方案
  • C++代码题部分(1)
  • LLM 模型部署难题的技术突破:从轻量化到分布式推理的全栈解决方案
  • AutoSAR(MCAL) --- ADC
  • Linux虚拟内存
  • 【C#】DevExpress.XtraEditors.MemoEdit memoEditLog控件讲解
  • AI服务器中,EEPROM有哪些部件使用,需要存储哪些信息?
  • Syzkaller实战教程2:运行环境配置+实例运行
  • 在Trae中使用MoonBit月兔
  • Android调用python库和方法的实现
  • 三十四、【Linux常用工具】rsync+inotify实时同步演示
  • GitHub使用小记——本地推送、外部拉取和分支重命名
  • Camera相机人脸识别系列专题分析之十九:MTK ISP6S平台FDNode传递三方FFD到APP流程解析
  • git本地仓库,工作区和暂存区的知识
  • llama factory本地部署常见问题
  • 用Python+MySQL实战解锁企业财务数据分析
  • 会吸的簸箕专利拆解:迷你真空组件的吸力控制与吸入口设计原理
  • Redis学习------缓存穿透
  • 数据建模怎么落地?从概念、逻辑到物理模型,一文讲请!
  • Prometheus-2--什么是Exporter是什么?
  • Spring boot 打包成docker image 镜像