这篇文章会详细介绍,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. 【Linux】 基于centos7.2 安装 LAMP

    服务器选择的阿里云ecs服务器,系统centos7.2版 一.连接服务器,检查当前系统环境 1.查看centos版本 [root@iZuf682jnxmszwd2gdvzh0Z ~]# cat /et ...

  2. java基础---->Runtime类的使用(一)

    这里面我们对java中的Runtime类做一个简单的了解介绍.若不常想到无常和死,虽有绝顶的聪明,照理说也和呆子一样. Runtimeo类的使用 一.得到系统内存的一些信息 @Test public ...

  3. 【大数据系列】HDFS安全模式

    一.什么是安全模式 安全模式时HDFS所处的一种特殊状态,在这种状态下,文件系统只接受读数据请求,而不接受删除.修改等变更请求.在NameNode主节点启动时,HDFS首先进入安全模式,DataNod ...

  4. JS笔记 - JQ事件委托( 适用于给动态生成的脚本元素添加事件)

    最近一段时间打了一个大仗,现在总算消停点,才有时间来做个总结吧算是: 移动端遇到一个项目,是一个列表的侧滑栏,在我这里用jq写的交互事件.自测各方面都挺好的,美滋滋的给了研发.研发也美滋滋的开始开发. ...

  5. 时间的类型的相互转换(date/String)及时区的比较

    String ->Date ->String @Test public void date() throws ParseException{ String sdate = "01 ...

  6. sencha touch 2.2 为list PullRefresh插件添加refreshFn方法

    sencha touch 2.2 list PullRefresh插件没有refreshFn方法 但是我们又需要他,所以需要自行扩展 代码如下 /** * 重写下拉刷新插件,以支持refreshFn事 ...

  7. dhroid - ioc高级(接口,对象注入)

    下面到了接口对象的注入了解冻吧,现在才是我们的重点,这才是ioc的核心思想,上面的都是android的辅助1.5 对象依赖问题 我们先来将一下对象对象依赖的重要性,很多同学可能只学了android没学 ...

  8. failed to register esriAddin

    ArcGIS AddIN开发遇到此种异常,目前有两种错误的可能:(1)项目名称好像不能为中文名,如果为中文名,请改正 (2)在Config.esriAddinx配置文件中,存在如下代码 <Tar ...

  9. zabbix触发器表达式详解

    Zabbix触发器的语法如下: {<server>:<key>.<function>(<parameter>)}<operator>< ...

  10. mongodb的学习笔记一(集合和文档的增删改查)

    1数据库的增删改查 一.增加一个数据库: use blog-----切换到指定的数据库,如果数据库不存在,则自动创建该数据库(新建的数据库,如果没有存储对应的集合,是不会显示出来的) 二.删除一个数据 ...