Spark 中的 Shuffle 是分布式数据交换的核心流程,从源码角度分析 Shuffle 的执行路径

Spark 中的 Shuffle 是分布式数据交换的核心流程,涉及多个组件的协同工作。为了深入理解其处理过程,我们可以从源码角度分析 Shuffle 的执行路径,分为 Shuffle Write 和 Shuffle Read 两个阶段。


1. Shuffle Write 阶段

Shuffle Write 的主要任务是将 Mapper 的数据按照分区规则(如 HashPartitioner)分割、排序并写入磁盘。

1.1 数据分区与序列化
  • 入口方法 :

    Mapper 阶段的 compute() 方法中调用了 ShuffleDependency 的相关逻辑。

    scala 复制代码
    val partition = partitioner.getPartition(key)

    数据会根据 partitioner(如 HashPartitioner 或自定义分区器)计算目标分区。

  • 序列化 :

    每条数据会通过 serializerInstance.serialize() 进行序列化,将数据转换成字节流以便后续写入。

1.2 数据排序与溢写(Spill)
  • 排序 :

    在 SortShuffleWriter 中,数据会被放入内存中的 PartitionedAppendOnlyMap 或 PartitionedPairBuffer 进行排序(根据键的自然顺序或用户指定的比较器)。

  • 溢写到磁盘 :

    当内存不足时,会触发溢写(spill)。溢写的数据会写到多个磁盘文件,每个文件对应多个分区。

1.3 合并分区数据
  • 归并操作 :
    在溢写文件较多时,Spark 会对这些临时文件执行归并排序,生成最终的分区文件。
    • 对于 BypassMergeSortShuffleWriter,会直接将分区文件写出,无需排序。
    • 对于 SortShuffleWriter,归并排序确保每个分区的数据有序。
1.4 写出索引文件
  • 最后,Shuffle Write 阶段会生成一个索引文件(shuffleId_0.index)和数据文件(shuffleId_0.data)。
    • 索引文件:记录每个分区在数据文件中的偏移量,用于快速定位分区数据。
    • 数据文件:存储分区后的数据。

2. Shuffle Read 阶段

Shuffle Read 阶段由 Reducer 执行,任务是从分布式存储中拉取相应分区的数据。

2.1 拉取数据
  • 入口方法 :

    Reducer 的 compute() 方法会调用 BlockStoreShuffleFetcher.fetch() 从 MapOutputTracker 获取每个分区的数据位置。

    scala 复制代码
    val blocksByAddress = mapOutputTracker.getMapSizesByExecutorId(shuffleId, reduceId)
  • 数据传输 :

    数据通过 Spark 的 BlockManager 拉取。如果目标数据在同一节点上,可以通过本地文件系统读取;如果在远程节点上,则通过 Netty 或 HTTP 传输。

2.2 数据解压与反序列化
  • 解压 :

    如果数据经过压缩(如 LZ4、Snappy),在读取时会被解压缩。

    • 压缩相关配置:spark.shuffle.compress=true,spark.shuffle.spill.compress=true。
  • 反序列化 :

    使用与 Shuffle Write 相同的序列化器(如 Kryo 或 JavaSerializer)将字节流转换回对象。

2.3 数据聚合与处理
  • 拉取到的数据会根据 Reducer 的逻辑(如 reduceByKey 或 aggregateByKey)进行聚合或排序处理。

3. 关键组件的协作关系

  • ShuffleManager :

    决定使用哪种类型的 Shuffle,如 SortShuffleManager 或 HashShuffleManager。

  • ShuffleWriter :

    负责数据写入磁盘,主要实现类:

    • BypassMergeSortShuffleWriter
    • SortShuffleWriter
  • ShuffleReader :

    负责从不同节点拉取数据,主要实现类:

    • BlockStoreShuffleReader
  • MapOutputTracker :

    负责跟踪每个 Mapper 的输出分区位置,Reducer 会通过它获取分区数据的位置。


4. Shuffle 设计的优缺点

特性 优点 缺点
分区文件索引 减少数据读取时的随机 I/O 开销 索引管理复杂度增加
排序优化 提高数据局部性和读取效率 需要更多的 CPU 和内存资源
溢写与归并 避免内存溢出,支持大规模数据处理 增加磁盘 I/O 开销
数据压缩 减少网络传输和存储空间 压缩和解压缩会增加 CPU 开销

源码路径及关键类

  1. Shuffle Write:

    • SortShuffleWriter:org.apache.spark.shuffle.sort.SortShuffleWriter
    • BypassMergeSortShuffleWriter:org.apache.spark.shuffle.sort.BypassMergeSortShuffleWriter
  2. Shuffle Read:

    • BlockStoreShuffleReader:org.apache.spark.shuffle.BlockStoreShuffleReader
  3. Shuffle 依赖与管理:

    • ShuffleDependency:org.apache.spark.shuffle.ShuffleDependency
    • ShuffleManager:org.apache.spark.shuffle.ShuffleManager

5. 性能优化方向

  1. 调优分区数:

    • 合理配置 spark.sql.shuffle.partitions 或 spark.default.parallelism,避免分区数过多或过少。
  2. 压缩与序列化:

    • 优化序列化器(推荐使用 Kryo),并启用压缩来减少网络开销。
  3. 内存管理:

    • 调整 spark.memory.fraction,确保 Shuffle 缓存有足够的内存。
  4. 使用外部 Shuffle 服务:

    • 启用 ExternalShuffleService,减轻 Executor 的内存和磁盘压力。

通过以上分析,可以从源码和优化角度全面理解 Spark Shuffle 的设计与工作原理。

相关推荐
狂奔solar6 小时前
眨眼检测——OCEC 112KB 模型重新定义实时眼部状态分类
大数据·人工智能·分类
Leo.yuan7 小时前
财务团队从“做表“到“分析“,缺的不是工具,是一套财经数智化体系
大数据
CS创新实验室7 小时前
为什么三台机器就能选出一个“带头大哥“?——Raft 一致性算法,一次讲透
分布式·操作系统
云表无代码开发7 小时前
日本开发者彻底破防:中文太强了!换成英文直接裂开
大数据·服务器·人工智能·microsoft·信息可视化
皮皮学姐分享-ppx8 小时前
地级市、省级人才政策强度测算(2000-2025)
大数据·数据库·人工智能·百度·高考
大大大大晴天8 小时前
面向湖仓一体的大数据安全治理:识别、授权、脱敏与审计
大数据
NailiConveyor9 小时前
输送机系统与WMS仓储管理系统集成的5个关键技术点
大数据·运维·自动化·制造·业界资讯
HZZD_HZZD9 小时前
碳排放核算只报一个数,审计凭什么信你?合众致达蒙特卡洛不确定性量化实测:25.2万度园区月账95%置信区间收窄至±2.4%
大数据·物联网
本人手速666+9 小时前
企微开发API如何设计客户冻结状态?WeComApi 在删除、投诉和异常客户场景中的自动化边界
运维·分布式·自动化·企业微信·企微外部群开发·wecomapi·企业微信二次开发
一只专注api接口开发的技术猿9 小时前
告别手工复制粘贴:搭建电商商品自动化监控与数据分析实践
大数据·运维·数据挖掘·数据分析·自动化