1、Shuffle流程

spark的shuffle过程如下图所示,和mapreduce中的类似,但在spark2.0及之后的版本中只存在SortShuffleManager而将原来的HashShuffleManager废弃掉(但是shuffleWriter的子类BypassMergeSortShuffleWriter和已经被废弃掉的HashShuffleWriter类似)。这样,每个mapTask在shuffle的sort阶段只会生成一个结果文件,单个文件按照partitionId分成多个region。reducer阶段根据partitionId来fetch对应的region数据。
整个shuffle过程分为两个阶段,write(核心)和read阶段,其中write阶段比较重要的实现类为ExternalSorter(后面会重点分析该类)。

2、Shuffle Write

  • BypassMergeSortShuffleWriter -
    这种方式是对partition(对应的reduce)数量较少且不需要map-side aggregation的shuffle优化,将每个partition的数据直接写到对应的文件,在所有数据都写入完成后进行一次合并,下面是部分代码:
[BypassMergeSortShuffleWriter]->write
public void write(Iterator<Product2<K, V>> records) throws IOException {

                                    ...

    partitionWriters = new DiskBlockObjectWriter[numPartitions];
/**
为每个partition创建一个DiskWriter用于写临时文件
**/
for (int i = ; i < numPartitions; i++) {
final Tuple2<TempShuffleBlockId, File> tempShuffleBlockIdPlusFile =
blockManager.diskBlockManager().createTempShuffleBlock();
final File file = tempShuffleBlockIdPlusFile._2();
final BlockId blockId = tempShuffleBlockIdPlusFile._1();
partitionWriters[i] =
blockManager.getDiskWriter(blockId, file, serInstance, fileBufferSize, writeMetrics);
}
...
/**
对每个record用对应的writer进行文件写入操作
**/
while (records.hasNext()) {
final Product2<K, V> record = records.next();
final K key = record._1();
partitionWriters[partitioner.getPartition(key)].write(key, record._2());
}
//flush
for (DiskBlockObjectWriter writer : partitionWriters) {
writer.commitAndClose();
}
/**
构造最终的输出文件实例,其中文件名为(reduceId为0):
"shuffle_" + shuffleId + "_" + mapId + "_" + reduceId
文件所在的local文件夹是根据该文件名的hash值确定。
1、如果运行在yarn上,yarn在启动的时候会根据配置项'LOCAL_DIRS'在本地创建
文件夹
**/
File output = shuffleBlockResolver.getDataFile(shuffleId, mapId);
//在实际结果文件名后加上uuid用于标识文件正在写入,结束后重命名
File tmp = Utils.tempFileWith(output);
try {
//合并每个partition对应的文件到一个文件中
partitionLengths = writePartitionedFile(tmp);
//将每个partition的offset写入index文件方便reduce端fetch数据
shuffleBlockResolver.writeIndexFileAndCommit(shuffleId, mapId, partitionLengths, tmp);
} finally {
if (tmp.exists() && !tmp.delete()) {
logger.error("Error while deleting temp file {}", tmp.getAbsolutePath());
}
}
mapStatus = MapStatus$.MODULE$.apply(blockManager.shuffleServerId(),
partitionLengths);
}
  • UnsafeShuffleWriter(详见project tungsten)

该writer可将数据序列化后写入到堆外内存,只需要按照partitionid对地址进行排序,整个过程不涉及反序列化。
条件
1、使用的序列化类需要支持object relocation.目前只能使用kryoSerializer
2、不需要map side aggregate即不能定义aggregator
3、partition数量不能大于支持的上限(2^24)
内存模型:
每条数据地址由一个64位的指针确定,其构成为:[24 bit partition number][13 bit memory page number][27 bit offset in page]
在内存为非8字节对齐的情况下,每个page的容量为227bits=128Mb,page总数为213,因此每个task可操作内存总量为:227*213bits=1Tb,在内存按字节对齐的情况下允许每个page的size有1g(即128*8,实际64位系统的内存都是8字节对齐的)的容量,数据存放在off heap上。在地址中加入partitionID 是为了排序阶段只需要对record的地址排序。

4、Shuffle过程中涉及到的几个参数

  • spark.shuffle.sort.bypassMergeThreshold
    当partition的数量小于该值并且不需要进行map-side aggregation时使用BypassMergeSortShuffleWriter来进行shuffle的write操作,默认值为200.
    [SortShuffleWriter]->shouldBypassMergeSort
def shouldBypassMergeSort(conf: SparkConf, dep: ShuffleDependency[_, _, _]): Boolean = {
if (dep.mapSideCombine) {
require(dep.aggregator.isDefined, "Map-side combine without Aggregator specified!")
false
} else {
val bypassMergeThreshold: Int = conf.getInt("spark.shuffle.sort.bypassMergeThreshold", )
dep.partitioner.numPartitions <= bypassMergeThreshold
}
}```
- *spark.shuffle.compress*、*spark.shuffle.file.buffer*
**[DiskBlockObjectWriter]->open**
def open(): DiskBlockObjectWriter = {
...
/**
'spark.shuffle.compress'-该参数决定是否对写入文件的序列化数据进行压缩。
'spark.shuffle.file.buffer'-设置buffer stream的buffersize,每write
一个byte时会检查当前buffer容量,容量满的时候则会flush到磁盘。该参数值在代码中
会乘以1024转换为字节长度。默认值为'32k',该值太大可能导致内存溢出。
**/
bs = compressStream(new BufferedOutputStream(ts, bufferSize))
...
}``` spark.file.transferTo
决定在使用BypassMergeWriter过程中,最后对文件进行合并时是否使用NIO方式进行file stream的copy。默认为true,在为false的情况下合并文件效率比较低(创建一个大小为8192的字节数组作为buffer,从in stream中读满后写入out stream,单线程读写),版本号为2..32的linux内核在使用NIO方式会产生bug,需要将该参数设置为false。 spark.shuffle.spill.numElementsForceSpillThreshold
在使用UnsafeShuffleWriter时,如果内存中的数据超过这个值则对当前内存数据进行排序并写入磁盘临时文件。

Spark Shuffle(ExternalSorter)的更多相关文章

  1. Spark Shuffle(一)ShuffleWrite:Executor如何将Shuffle的结果进行归并写到数据文件中去(转载)

    转载自:https://blog.csdn.net/raintungli/article/details/70807376 当Executor进行reduce运算的时候,生成运算结果的临时Shuffl ...

  2. Spark Shuffle(三)Executor是如何fetch shuffle的数据文件(转载)

    1. 前言 在前面的博客中讨论了Executor, Driver之间如何汇报Executor生成的Shuffle的数据文件,以及Executor获取到Shuffle的数据文件的分布,那么Executo ...

  3. Spark Shuffle(二)Executor、Driver之间Shuffle结果消息传递、追踪(转载)

    1. 前言 在博客里介绍了ShuffleWrite关于shuffleMapTask如何运行,输出Shuffle结果到Shuffle_shuffleId_mapId_0.data数据文件中,每个exec ...

  4. Dream_Spark-----Spark 定制版:003~Spark Streaming(三)

    Spark 定制版:003~Spark Streaming(三) 本讲内容: a. Spark Streaming Job 架构和运行机制 b. Spark Streaming Job 容错架构和运行 ...

  5. .Spark Streaming(上)--实时流计算Spark Streaming原理介

    Spark入门实战系列--7.Spark Streaming(上)--实时流计算Spark Streaming原理介绍 http://www.cnblogs.com/shishanyuan/p/474 ...

  6. Spark教程——(11)Spark程序local模式执行、cluster模式执行以及Oozie/Hue执行的设置方式

    本地执行Spark SQL程序: package com.fc //import common.util.{phoenixConnectMode, timeUtil} import org.apach ...

  7. Spark源码分析之Spark Shell(下)

    继上次的Spark-shell脚本源码分析,还剩下后面半段.由于上次涉及了不少shell的基本内容,因此就把trap和stty放在这篇来讲述. 上篇回顾:Spark源码分析之Spark Shell(上 ...

  8. Spark学习之Spark Streaming(9)

    Spark学习之Spark Streaming(9) 1. Spark Streaming允许用户使用一套和批处理非常接近的API来编写流式计算应用,这就可以大量重用批处理应用的技术甚至代码. 2. ...

  9. Spark学习之Spark SQL(8)

    Spark学习之Spark SQL(8) 1. Spark用来操作结构化和半结构化数据的接口--Spark SQL. 2. Spark SQL的三大功能 2.1 Spark SQL可以从各种结构化数据 ...

随机推荐

  1. django-south使用 [转]

    转自: http://alexliyu.blog.163.com/blog/static/16275449620126239949478/ 使用South之前铭记:请你一定要相信他的能力,抛弃对他的不 ...

  2. WPF 自定义命令 以及 命令的启用与禁用

    自定义命令:     在WPF中有5个命令类(ApplicationCommands.NavigationCommands.EditingCommands.ComponentCommands 以及 M ...

  3. Neo4j简单的样例

    系统环境: Ubuntu 04.10 x64 一:安装 下载最新版:neo4j-community-2.2.3-unix.tar.gz  解压 cd neo4j-community-2.2.3/bin ...

  4. mybatis-spring-1.2.1 jar下载、源码下载

    http://www.everycoding.com/maven2/org/mybatis/mybatis-spring/1.2.1.html

  5. stringstream读入每行数据

    做了下阿里的编程测试题,就30分钟,不是正常的输入输入,直接给一个数组作为输入. 于是带想题和处理数据花了20分钟,最后10分钟搞一个dij模版, 竟然只过了66%,应该是我数组开小了. 题目数据量没 ...

  6. N小时改变一次url时间戳的方法

    //为url添加时间戳//time 为多长时间改变一次时间戳,以小时为单位function setTimeStamp(url, time){    var time = time || 4,      ...

  7. Cordova 3.0 初步使用

    主要参考 http://docs.phonegap.com/en/3.0.0/guide_cli_index.md.html#The%20Command-line%20Interface Cordov ...

  8. element.style{}

    有时在写css样式,并调试时,会出现很不可思议的现象,比如:我们定义了一个<div class=”aaa”></div>,在css中定义样式,.aaa{width:500px; ...

  9. ATDD和TDD的区别是什么?

    最近看到一个新名词"ATDD",全称"Acceptance Test Driven Development ",中文称"验收测试驱动开发". ...

  10. 【BZOJ3012】[Usaco2012 Dec]First! Trie树+拓补排序

    [BZOJ3012][Usaco2012 Dec]First! Description Bessie has been playing with strings again. She found th ...