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. 对于MathType中公式与文字错位的问题怎么解决

    MathType是强大的数学公式编辑器,与常见的文字处理软件和演示程序配合使用,能够在各种文档中加入复杂的数学公式和符号,可用在编辑数学试卷.书籍.报刊.论文.幻灯演示等方面,是编辑数学资料的得力工具 ...

  2. 关于BSTR数据类型

    关于BSTR数据类型 - 极品垃圾 - C++博客 http://www.cppblog.com/bestcln/articles/82712.html VC++常用数据类型及其操作详解(非常经典,共 ...

  3. [转]ASP.NET MVC 5 - 查询Details和Delete方法

    在这部分教程中,接下来我们将讨论自动生成的Details和Delete方法. 查询Details和Delete方法 打开Movie控制器并查看Details方法. public ActionResul ...

  4. android webView不简单

    手机屏幕大小非常伤程序猿 励志成为一名Javaproject师的我.真的被它伤到了,不仅由于webView的强大.并且这个内容适合各样屏幕大小问题. 想当年苹果project师嘲笑安卓project师 ...

  5. Mac普通用户修改了/etc/sudoers文件的解决办法

    1.开启 Root 账户 打开“系统偏好设置”,进入“用户与群组”面板,记得把面板左下角的小锁打开,然后选择面板里的“登录选项”.在面板右边你会看到“网络账户服务 器”,点击它旁边的“加入…”按钮,再 ...

  6. Python tips: 什么是*args和**kwargs?

    推荐查看:https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00137473844 ...

  7. 百度地图出现UnsatisfiedLinkError: Native method not found: com.baidu...

    这是由于导入os文件方式引起 解决办法有两种: 第一种: 弄一个文件夹so/armeabi,把所有的so全部放进去.结构就是so/armeabi/a.so,b.so,c.so 然后压缩这个文件夹成为s ...

  8. linux的setup命令设置网卡和防火墙等

    以前在centos上配置网卡都是纯命令行,今天发现linux原来还有一个setup那么好用的命令,真是相见恨晚,以后防火墙.网卡.其他网络配置.系统配置(开机启动项)都可用他来完成了

  9. Ubuntu 14.04.02 安装openvswitch-2.3.1

    Open vSwitch安装 安装好操作系统 # lsb_release -a LSB Version: core-2.0-amd64:core-2.0-noarch:core-3.0-amd64:c ...

  10. LeetCode——Basic Calculator II

    Description: Implement a basic calculator to evaluate a simple expression string. The expression str ...