Spark 分区:shuffle、倾斜与计划排障
分区、Shuffle 与执行计划
本例的去重、冲突隔离与收入聚合见订单工程的 batch.py、config.json 和 orders.csv;下文沿这组输入解释分区与诊断。
前置:会读Python函数、SQL分组和文件路径。本文固定Apache Spark/PySpark 3.5.6版本族,运行前应核对所在集群版本;不是对4.x的兼容承诺。本轮只写作与静态解析,示例未执行,不启动Spark、JVM或外部服务。
Spark shuffle 边界
Spark shuffle 边界来自数据必须按新键重新分布:groupBy客户、按事件ID去重、普通大表join都可能引入Exchange,随后有网络传输、序列化和磁盘溢写。窄依赖如filter通常可在当前分区流水执行。stage通常在shuffle依赖处分开,但不能把每个DataFrame语句等同一个stage。
本例先按规范化业务字段去重,再按event_id找冲突,最后按日期与客户聚合;每个键的含义不同,不能为了少一次shuffle就删掉冲突检测。先过滤不需要的行和列有助于减少数据量,但不能移动会改变语义的过滤。诊断时看物理计划的Exchange、输入输出行数和spill,再决定优化哪里。
Spark 分区与排序
Spark 分区与排序是两个独立维度。repartition(n, key)按键重新分布,不保证分区内排序;sortWithinPartitions仅局部有序,不保证跨分区全局顺序。orderBy才表达全局排序关系,代价通常包含shuffle;写成多个文件后,文件集合的读取顺序仍没有承诺。
结果正确性应比较按完整键排序后的行与值,不应比较part文件名。相同排序键的行需要额外稳定的决胜列,才有唯一展示顺序。dropDuplicates([event_id])不按输入顺序挑选“最新”记录;真正最新应有业务版本、时间与稳定决胜键,再用窗口row_number。我们的策略是冲突整组隔离,不随机取first。
Spark repartition 与 coalesce
Spark repartition 与 coalesce都改变分区数,但成本不同。repartition通常shuffle重分布,既能增加并行度也能缓解部分分区过大;coalesce常用于减少分区,通常避免全量shuffle,却可能把工作压到少量task。coalesce(1)可让演示只写一个数据分片,却会在大数据上形成瓶颈,因此示例不强制单文件。
spark.sql.shuffle.partitions在本地练习设4,表达初始SQL shuffle并行度,不代表最终永远4个文件。输入文件切分、AQE、写入目录分区列都影响task与文件数。目录分区partitionBy(day)是存储布局,也不是承诺每个day只产一个文件。
Spark AQE 与倾斜
Spark AQE 与倾斜处理依赖运行时统计,可合并shuffle后的小分区,调整部分连接策略,并在适用条件下拆分倾斜连接分区。Spark 3.5族默认启用AQE,但它不修复错误join条件、无限状态或错误去重语义,也不承诺消除所有热点键聚合。
如果一个客户占九成订单,聚合可能仍受热点键限制;先用task耗时和分区大小证据识别。加盐聚合只有在可分解函数与二次归并语义正确时才适用,不能把avg直接平均平均数,也不能随意给distinct计数加盐。broadcast hint也不保证被选中,强制广播过大维表可耗尽执行器内存。比较最终物理计划和真实指标,不把开关数量当性能证据。
资料与版本核对
英文以官方3.5.6固定版本文档为准;中文作者资料用于术语与入门路径,不据旧版教程推断3.5新增API。核对日期:2026-10-17。
诊断、资源失败与可恢复边界
Spark explain 诊断顺序
Spark explain 诊断顺序先看输入schema与行数契约,再读逻辑计划和物理计划,最后核对运行指标。explain(mode="formatted")展示算子与Exchange;AQE执行前的计划可能还没最终确定。计划只描述执行方式,不能证明结果金额正确,更不能用尚未执行的计划猜耗时。
对本例可人工定位去重、冲突groupBy、anti join与最终聚合。若结果多一倍,先查输入重复和join基数;若某客户收入被放大,先验证维表键唯一,再考虑executor数量。监控应包括输入行、隔离行、接受事件与收入守恒,不只看作业成功。示例未运行,因此这里没有伪造UI截图、耗时或stage数量。
Spark OOM 与重试
Spark OOM 与重试应先定位进程。driver因collect或过大任务元数据耗尽,与executor因大分区、广播或状态耗尽的处理不同。executor丢失可重算部分任务,但外部副作用可能重复;自动重试不代表错误代码终会正确。反复重试同一个过大热点分区可能只增加成本。
先记录失败stage/task、输入分片、executor日志中的首个异常和spill,再判断是否需要减少宽行、过滤、重分布或调整资源。不要把catch所有异常后返回空表当恢复,那会让财务报表成功但少收入。畸形字段应按显式质量策略隔离,缺文件或结构破坏则失败并阻止发布。
Spark 结果提交边界
Spark 结果提交边界由数据源、文件系统和sink提交协议共同决定,不能由Spark品牌推导全链路exactly-once。幂等需要稳定业务键、输入快照与可判定的提交状态;目录_SUCCESS通常表示该次文件写入完成,不等于多个输出已一起提交。
本例的两个action可能重新读取源;因此自跑过程中不可修改orders.csv。输出存在时拒绝覆盖,失败后换新目录完整重算。若要生产化,应先验证源快照、不可变批次ID、清单发布和下游消费约定;清理旧输出必须等发布与保留策略允许。checkpoint丢失后的流重放还需考虑源的保留期,不能承诺无限恢复。
资料与版本核对
英文以官方3.5.6固定版本文档为准;中文作者资料用于术语与入门路径,不据旧版教程推断3.5新增API。核对日期:2026-10-17。
继续阅读
- 批处理完整工程
- DataFrame与惰性执行
- 分区与流语义