目录
前提知识:
宽依赖:上游RDD中的一个分区中的数据,被下游RDD的多个分区所共享,称为一对多
窄依赖:上游RDD中的一个分区中的数据,被下游RDD的一个分区所共享,称为一对一
Shuffle图解
SortShuffleManager
Sort Shuffle
流程图
Shuffle Write
task阶段划分的时候,会创建ShuffleMapTask和ResultTask,在ShuffleMapTask进行write操作ResultTask进行read的操作,在write时,会根据Handle处理器决定调用哪个Writer类,有三种处理器SerializedShuffleHandle、BypassMergeSortShuffleHandle、BaseShuffleHandle,这三个处理器分别对应UnsafeShuffleWriter、BypassMergeSortShuffleWriter、SortShuffleWriter。如果是reduceBykey有预聚合的算子,走SortShuffleWriter。在写数据时会判断是否shouldCombine,如果需要combin,根据key得到分区,修改对应key的value,并判断是否要Spill磁盘,当
1.elementsRead % 32 == 0 && currentMemory >= 5*1024*1024 (5M)当满足1时,同时满足2.申请的资源不足
2.shouldSpill = currentMemory >= myMemoryThreshold 当数据大于Int的最大值时候,强制溢写 3.shouldSpill = shouldSpill || _elementsRead > numElementsForceSpillThreshold如果只是数据没有spill到磁盘,only-memory data,不进行merge,如果需要order(reduceByKey,但是数据量很小没有spill)的话,需要现根据分区id再根据key排序
如果数据spill到磁盘,当溢写到磁盘时进行merge操作(磁盘文件和内存都merge ),现根据分区再根据key进行排序 归并排序使用的排序算法是堆排序,先写入缓冲区(32k),然后溢写到磁盘,最后释放内存。
最后将早期的IndexFile和dataFile删除,将indexTmp.renameTo(indexFile)、dataTmp.renameTo(dataFile)
Shuffle Read
读磁盘文件有个缓冲区48M
BypassMergeSortShuffle
流程图
Bypass机制触发条件
if (dep.mapSideCombine) {
false
} else {
val bypassMergeThreshold: Int = conf.get(config.SHUFFLE_SORT_BYPASS_MERGE_THRESHOLD)
dep.partitioner.numPartitions <= bypassMergeThreshold
}
private[spark] val SHUFFLE_SORT_BYPASS_MERGE_THRESHOLD =
ConfigBuilder("spark.shuffle.sort.bypassMergeThreshold")
.doc("In the sort-based shuffle manager, avoid merge-sorting data if there is no " +
"map-side aggregation and there are at most this many reduce partitions")
.version("1.1.1")
.intConf
.createWithDefault(200)
1.不是预聚合算子
2.下游分区数量数量小于等于spark.shuffle.sort.bypassMergeThreshold参数的值。 200
Spark Shuffle处理器
处理器 | 调用类 | 条件 |
unsafeShuffleHandle | UnsafeShuffleWriter | 1.序列化重定位 kryo支持,java不支持 2.不能使用预聚合 3.下游分区数量小于或等于16777215 |
bypassMergeSortHandle | BypassMergeSortShuffleWriter | 1.不能使用预聚合 2.下游分区数量小于等于200 例如:groupbykey |
BaseShuffleHandle | BaseShuffleHandle | 其他情况 例如:reduceByKey |
SortShuffleManager中getWriter方法
handle match {
case unsafeShuffleHandle: SerializedShuffleHandle[K @unchecked, V @unchecked] =>
new UnsafeShuffleWriter(
env.blockManager,
context.taskMemoryManager(),
unsafeShuffleHandle,
mapId,
context,
env.conf,
metrics,
shuffleExecutorComponents)
case bypassMergeSortHandle: BypassMergeSortShuffleHandle[K @unchecked, V @unchecked] =>
new BypassMergeSortShuffleWriter(
env.blockManager,
bypassMergeSortHandle,
mapId,
env.conf,
metrics,
shuffleExecutorComponents)
case other: BaseShuffleHandle[K @unchecked, V @unchecked, _] =>
new SortShuffleWriter(
shuffleBlockResolver, other, mapId, context, shuffleExecutorComponents)
}
bypass不用进行排序所以效率比sort shuffle高,但是和没优化的HashShuffle一样,会产生很多的临时文件,所以不适用分区特别多的情况