文章目录
一、概述
不同于Spark静态RDD shuffle过程需要创建分段文件及分段索引,流式shuffle通过RPC方式实时发送数据,使得数据处理的时效性更高,例如可以达到~100ms级。
本文主要介绍Spark Structured Streaming 2.4.0 中,Continous流处理模式新增流式Repartition和对应Shuffle操作,为后续流式Shuffle源码扩展提供理论基础。
下一篇文章,我们会从应用项目的需求出发,探讨如何进行流式Shuffle源码扩展,包括以下三个扩展功能:
- 流式Shuffle支持多分区(开源版本仅支持shuffle输出端为1个分区)
- Continuous支持侧边输出(1个输入源对应多个输出,多线程执行)
- 支持下游侧边输出分区偏好设置(如果多个目标侧边分区在同一远端主机,且具有相同的Partitioner,将数条重复数据合并为一条发送,提升性能)
二、 原生ContinuousCoaleseExec执行相关
如何创建ContinuousCoaleseExec及其对应RDD:
1、创建ContinuousCoaleseEexc
- 在Spark 2.3.0流式处理中,默认不支持Repartition,IncrementalExecution#planner中也没有流式Repartition相关的处理
- 在Spark 2.4.0流式处理中,Continuous连续处理模式已经支持Repartition操作
- 静态Repartition操作绑定的是ShuffleExchangeExec或CoalesceExec
- ContinuousCoaleseEexc是Continous模式中,Reparation操作的绑定SparkPlan物理计划
- 从以下创建算子的代码中可以看到,spark 2.4.0 continuous连续处理时,目前只支持reparation为1的操作(包括流式Aggregation,只要涉及到重分区):
// Spark 2.4.0流处理中,Continuous连续处理模式Repartition在DataSourceV2Strategy中定义
object DataSourceV2Strategy extends Strategy {
...
override def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
...
case Repartition(1, false, child) =>
val isContinuous = child.collectFirst {
case StreamingDataSourceV2Relation(_, _, _, r: ContinuousReader) => r
}.isDefined
if (isContinuous) {
ContinuousCoalesceExec(1, planLater(child)) :: Nil
} else {
Nil
}
}
}
// 静态Repartition绑定策略在BasicOperators中定义
object BasicOperators extends Strategy {
case logical.Repartition(numPartitions, shuffle, child) =>
if (shuffle) {
// Dataset#repartition()默认走ShuffleExchangeExec
ShuffleExchangeExec(RoundRobinPartitioning(numPartitions), planLater(child)) :: Nil
} else {
// 分区合并
execution.CoalesceExec(numPartitions, planLater(child)) :: Nil
}
}
2、ContinuousCoaleseEexc执行的操作
- ContinuousCoalesceExec类实现较为简单,主要是重写#doExecutue,创建一个ContinuousCoalesceRDD,用来实现Reparation需要的流式Shuffle操作
- Continuous