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. 九度 1537:买卖股票(区间DP)

    总结 1. 更新动规矩阵时, 不要 push 更新, 要用 pull更新. push 更新容易让逻辑出问题, 自己卡了很久, 改用 pull 就变得很顺利了 2. acm 题, 空间至多是百万, 再网 ...

  2. 数据库客户端快捷键(oracle+sybase)

    PL/SQL: 选中单行:鼠标三连击某行,那么这一行即被选中. 执行脚本:F8

  3. Lua脚本和C++交互(二)

    上一节讲了一些基本的Lua应用,下面,我要强调一下,Lua的栈的一些概念,因为这个确实很重要,你会经常用到.熟练使用Lua,最重要的就是要时刻知道什么时候栈里面的数据是什么顺序,都是什么.如果你能熟练 ...

  4. js 判断数据类型的几种方法

    判断js中的数据类型有一下几种方法:typeof.instanceof. constructor. prototype. $.type()/jquery.type(),接下来主要比较一下这几种方法的异 ...

  5. PyQt4开关按钮ToggleButton

    PyQt4没有开关按钮部件.但是我们可以使用在特殊状态下的QPushButton部件来创建开关按钮.而所谓的开关按钮就是一个具有按下和未按下两种状态的普通赶牛.用户可以通过单击按钮来切换其开或者关的状 ...

  6. mysql show processlist 命令检查mysql lock

    processlist命令的输出结果显示了有哪些线程在运行,可以帮助识别出有问题的查询语句,两种方式使用这个命令. 1. 进入mysql/bin目录下输入mysqladmin processlist; ...

  7. poj_1743 后缀数组

    题目大意 给定一串数字,长度为N.定义数字中的某个连续的子串为一个"theme",只要子串满足: (1)长度 >= 5 (2)和该子串相同或者该子串的“变种串”在整串数字中出 ...

  8. Kotlin——初级篇(一):最详细的环境搭建

    众所周知,Kotlin出来已经良久了.Kotlin有着众多优势,不管是用于Android开发中,还是Java开发,都能缩减很大的代码量,大大提高了工作效率.而小生本人也是才从忙碌的个工作中抽身出来,有 ...

  9. python实现HTTP代理的思路和Demo

    一.首先什么是代理: 代理其实就是中间转发的那个玩意,所以在代码逻辑上也是如此的. 二.Python写http代理的基本逻辑: (1)接受浏览器发出的请求,解析,拼凑成该有的样子,然后使用套接字发出去 ...

  10. Android EditText光标颜色 与inputType

    1.EditText有一个属性:android:textCursorDrawable,这个属性是用来控制光标颜色的   android:textCursorDrawable="@null&q ...