这篇文章会详细介绍,Sort Based Shuffle Write 阶段是如何进行落磁盘的

流程分析

入口处:

org.apache.spark.scheduler.ShuffleMapTask.runTask

runTask对应的代码为:

val manager = SparkEnv.get.shuffleManager
writer = manager.getWriter[Any, Any](
dep.shuffleHandle,
partitionId,
context)
writer.write(rdd.iterator(partition, context).asInstanceOf[Iterator[_ <: Product2[Any, Any]]])
writer.stop(success = true).get

这里manager 拿到的是

org.apache.spark.shuffle.sort.SortShuffleWriter

我们看他是如何拿到可以写磁盘的那个sorter的。我们分析的线路假设需要做mapSideCombine

sorter = if (dep.mapSideCombine) {
require(dep.aggregator.isDefined, "Map-side combine without Aggregator specified!")
new ExternalSorter[K, V, C](
dep.aggregator,
Some(dep.partitioner),
dep.keyOrdering, de.serializer)

接着将map的输出放到sorter当中:

sorter.insertAll(records)

其中insertAll 的流程是这样的:

while (records.hasNext) {
addElementsRead() kv = records.next()
map.changeValue((getPartition(kv._1), kv._1), update)
maybeSpillCollection(usingMap = true)}

里面的map 其实就是PartitionedAppendOnlyMap,这个是全内存的一个结构。当把这个写满了,才会触发spill操作。你可以看到maybeSpillCollection在PartitionedAppendOnlyMap每次更新后都会被调用。

一旦发生spill后,产生的文件名称是:

    "temp_shuffle_" + id

逻辑在这:

val (blockId, file) = diskBlockManager.createTempShuffleBlock() 

  def createTempShuffleBlock(): (TempShuffleBlockId, File) = {
var blockId = new TempShuffleBlockId(UUID.randomUUID())
while (getFile(blockId).exists()) {
blockId = new TempShuffleBlockId(UUID.randomUUID())
}
(blockId, getFile(blockId))
}

产生的所有 spill文件被被记录在一个数组里:

  private val spills = new ArrayBuffer[SpilledFile]

迭代完一个task对应的partition数据后,会做merge操作,把磁盘上的spill文件和内存的,迭代处理,得到一个新的iterator,这个iterator的元素会是这个样子的:

(p, mergeWithAggregation(
iterators,
aggregator.get.mergeCombiners, keyComparator,
ordering.isDefined))

其中p 是reduce 对应的partitionId, p对应的所有数据都会在其对应的iterator中。

接着会获得最后的输出文件名:

val outputFile = shuffleBlockResolver.getDataFile(dep.shuffleId, mapId)

文件名格式会是这样的:

 "shuffle_" + shuffleId + "_" + mapId + "_" + reduceId + ".data"

其中reduceId 是一个固定值NOOP_REDUCE_ID,默认为0。

然后开始真实写入文件

  val partitionLengths = sorter.writePartitionedFile(
blockId,
context,
outputFile)

写入文件的过程过程是这样的:

for ((id, elements) <- this.partitionedIterator) {
if (elements.hasNext) { val writer = blockManager.getDiskWriter(blockId,
outputFile,
serInstance,
fileBufferSize,
context.taskMetrics.shuffleWriteMetrics.get) for (elem <- elements) {
writer.write(elem._1, elem._2)
} writer.commitAndClose()
val segment = writer.fileSegment()
lengths(id) = segment.length
}
}
刚刚我们说了,这个 this.partitionedIterator 其实内部元素是reduce partitionID -> 实际record 的 iterator,所以它其实是顺序写每个分区的记录,写完形成一个fileSegment,并且记录偏移量。这样后续每个的reduce就可以根据偏移量拿到自己需要的数据。对应的文件名,前面也提到了,是:
"shuffle_" + shuffleId + "_" + mapId + "_" + NOOP_REDUCE_ID + ".data"

刚刚我们说偏移量,其实是存在内存里的,所以接着要持久化,通过下面的writeIndexFile来完成:

shuffleBlockResolver.writeIndexFile(
dep.shuffleId,
mapId,
partitionLengths)

具体的文件名是:

  "shuffle_" + shuffleId + "_" + mapId + "_" + NOOP_REDUCE_ID + ".index"

至此,一个task的写入操作完成,对应一个文件。

最终结论

所以最后的结论是,一个Executor 最终对应的文件数应该是:

MapNum (注:不包含index文件)

同时持有并且会进行写入的文件数最多为::

 CoreNum

Spark Shuffle Write阶段磁盘文件分析的更多相关文章

  1. Spark Shuffle之Sort Shuffle

    源文件放在github,随着理解的深入,不断更新,如有谬误之处,欢迎指正.原文链接https://github.com/jacksu/utils4s/blob/master/spark-knowled ...

  2. spark shuffle

    Spark Shuffle 1. Shuffle相关 当Map的输出结果要被Reduce使用时,输出结果需要按key哈希,并且分发到每一个Reducer上去,这个过程就是shuffle.由于shuff ...

  3. spark shuffle写操作之SortShuffleWriter

    提出问题 1. spark shuffle的预聚合操作是如何做的,其中底层的数据结构是什么?在数据写入到内存中有预聚合,在读溢出文件合并到最终的文件时是否也有预聚合操作? 2. shuffle数据的排 ...

  4. Spark Shuffle机制详细源码解析

    Shuffle过程主要分为Shuffle write和Shuffle read两个阶段,2.0版本之后hash shuffle被删除,只保留sort shuffle,下面结合代码分析: 1.Shuff ...

  5. Spark Shuffle原理、Shuffle操作问题解决和参数调优

    摘要: 1 shuffle原理 1.1 mapreduce的shuffle原理 1.1.1 map task端操作 1.1.2 reduce task端操作 1.2 spark现在的SortShuff ...

  6. MapReduce Shuffle原理 与 Spark Shuffle原理

    MapReduce的Shuffle过程介绍 Shuffle的本义是洗牌.混洗,把一组有一定规则的数据尽量转换成一组无规则的数据,越随机越好.MapReduce中的Shuffle更像是洗牌的逆过程,把一 ...

  7. Spark Shuffle实现

    Apache Spark探秘:Spark Shuffle实现 http://dongxicheng.org/framework-on-yarn/apache-spark-shuffle-details ...

  8. Apache 流框架 Flink,Spark Streaming,Storm对比分析(一)

    本文由  网易云发布. 1.Flink架构及特性分析 Flink是个相当早的项目,开始于2008年,但只在最近才得到注意.Flink是原生的流处理系统,提供high level的API.Flink也提 ...

  9. Spark中的Spark Shuffle详解

    Shuffle简介 Shuffle描述着数据从map task输出到reduce task输入的这段过程.shuffle是连接Map和Reduce之间的桥梁,Map的输出要用到Reduce中必须经过s ...

随机推荐

  1. 【easyswoole】 解决安装报错

    在使用swoole 创建项目时候,报错 创建命令 composer create-project easyswoole/app easyswoole 错误信息: 解决办法,切换composer 源 镜 ...

  2. JavaScript 中的 Map

    很多编程语言中都有类似Map这种 键-值对 的数据结构. 可惜,JavaScript没有. 幸运的是,可以自己构建一个Map对象. 对象的定义 <script type="text/j ...

  3. C和C++书籍推荐

    http://bestcbooks.com/recommend/most-influential-book/ http://www.ruanyifeng.com/blog/2011/09/c_prog ...

  4. 题目1454:Piggy-Bank(完全背包问题)

    题目链接:http://ac.jobdu.com/problem.php?pid=1454 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...

  5. Visual Studio 2015打开ASP.NET MVC的View提示"Object reference not set to an instance of an object"错误的解决方案

    使用Visual Studio 2013打开没有问题,但Visual Studio 2015打开cshtml就会提示"Object reference not set to an insta ...

  6. centos7搭建docker私有仓库

    1.环境: [root@docker02 anchors]# cat /etc/redhat-release CentOS Linux release 7.3.1611 (Core) [root@do ...

  7. Asp.net MVC]Asp.net MVC5系列——Routing特性

    目录 概述 路由特性 使用路由 可选参数和参数的默认值 路由前缀 默认路由 路由约束 自定义路由约束 路由名 区域(Area) 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列— ...

  8. TFS二次开发02——连接TFS

    在上一篇<TFS二次开发01——TeamProjectsPicher>介绍了  TeamProjectsPicher 对象,使用该对象可以很简单的实现连接TFS. 但是如果我们要实现自定义 ...

  9. Java-字符串加密

    1设计思想: 改程序是对小写的a到z进行加密,输入一段字符串str,输入加密的密匙k,判断录入的的字符与 ‘z’-k+1的大小,比其小的直接加上密匙转化为新的字符,大于的加(k-26)将最后几位字符转 ...

  10. HDU 3903 Trigonometric Function(数学定理)

    Trigonometric Function Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 125536/65536 K (Java/Oth ...