0. Shuffle概述

  要理解什么是Shuffle,首先介绍大数据与分布式。我们知道大数据的存储是分布式存储,大数据的计算框架是分布式的计算框架。分布式必然存在数据的交互传输,简言之Shuffle就是分布式中数据交互传输的过程。

  如下图所示,Stage 0的输出数据需要经过shuffle Writer写出到Block中,Stage 1的输入数据需要从Block中读入,这一中间结果的写出读入过程就是一次Shuffle。

  图1

  那么问题来了,为什么Stage 0的数据不能直接交给Stage 1处理,非要经过Shuffle过程呢?

  这里我们从Stage的划分开始说起,Stage划分的依据是RDD的宽窄依赖,宽窄依赖的划分依据参考此文:https://www.cnblogs.com/beichenroot/p/11414173.html

  RDD之所以会存在宽窄依赖,与Spark天生的属性有关,Spark是分布式计算引擎,RDD是弹性分布式数据集,是Spark中处理数据的逻辑单位。RDD的数据存储方式如下图所示:图中RDD数据分别存储在三个节点不同的Executor的分区(Partition)中,分区可以理解为Block的不同表述,一个分区对应一个Block,这三个不同节点上的分区一起构成一个RDD。

  回到正题,由于RDD的数据存在不同的节点上,分布式计算在不同节点上并行进行,如果是窄依赖,即分区间的数据不需要交互,则数据在本分区内操作;如果是宽依赖,数据需要往其他分区传送,则必然产生数据的读写,则存在Shuffle。

1. Shuffle的重要性

  由图1Shuffle的过程可知,Shuffle过程包含三部分:

  1. Shuffle的Wiiter

  2. 网络传输

  3. Shuffle的Read

  这三部分操作包含内存操作、磁盘I/O、网络I/O以及JVM管理,影响了Spark应用程序的绝大部分效率。对于好的程序代码,大部分的性能(95%)都消耗在Shuffle阶段的本地写磁盘文件、网络传输数据及抓取数据中。所以Spark针对Shuffle过程做了大量优化。

2. Spark Shuffle的发展过程

  Spark Shuffle早期采用的是HashShuffle,HashShuffle最大的问题是产生的文件数量(Mapper分片数量 * Reducer的分片数量)太多,导致大量的IO内存消耗以及沉重的GC负担;

  Spark采用SortedBasedShuffle根据Map Task的数量来生成文件,如下图所示,每个Map任务会生成两个文件,一个是数据文件分片写入,一个是偏移量文件,记录了每个分片的相关信息。Reduce任务读取数据时首先从偏移量文件中获取数据的位置,再到数据文件中读取数据。SortedBasedShuffle将Shuffle中的文件数量降低到了2 * Map任务个数,带来的优化如下:

  1. Mapper端占用内存变少

  2. 增加了Spark处理大规模数据的能力(可并行的任务数增加)

  3. Reducer端抓取数据的次数变少

  4. 网络通道的句柄变少

  5. 减少了数据本身对内存的消耗

Spark Shuffle详细内容可参考此文:https://www.cnblogs.com/itboys/p/9226479.html

3. Shuffle与Storage模块间的交互

  Spark中存储模块被抽象成Storage,Storage代表着Spark中的数据存储系统,负责管理数据块(Block),Block是存取数据的最小单元,等价于RDD中的Partition。

  Storage抽象模块分为两个层次:

  1. 通信层:通信层是典型的主从结构,Master和Slave之间传输控制和状态信息。通信层主要由BlockManager、BlockManagerMaster、BlockManagerMasterEndPoint、BlockManagerSlaverEndPoint等类实现。

  2. 存储层:负责把数据存储到内存、磁盘或者堆外内存中,有时还需要为数据在远程节点上生成副本。Spark存储层的实现类有DiskStore和MemoryStore。

  Shuffle过程中进行的数据读写实质是通过操作BlockManager接口来实现的。

3.1 BlockManager架构  

  1. Application启动时会在SparkEnv中注册BlockManagerMaster以及MapOutputTracker

    a. BlockManagerMaster:对整个集群的Block数据进行管理

    b. MapOutputTracker:跟踪所有的Mapper输出

  2. 构建BlockManagerMaster时会创建BlockManagerMasterEndPoint,BlockManagerMasterEndPoint本身是一个消息体,负责通过远程通信的方式管理所有节点的BlockManager。

  3. 没启动一个ExecutorBackend,都会实例化BlockManager,并通过远程通信的方式注册给BlockManagerMaster;实质上是Executor中的BlockManager在启动时注册给了Driver上的BlockManagerMasterEndPoint。

  4. MemoryStore是BlockManager中负责内存数据存储和读写的类。

  5. DiskStore是BlockManager中负责磁盘数据存储和读写的类。

  6. DiskBlockManager:管理Logical Block与Disk上的Physical Block之间映射关系并负责磁盘文件的创建、读写等。

3.2 Shuffle与BlockManager交互

3.2.1 Shuffle写数据

  基于Sort的Shuffle实现的ShuffleHandle包含BypassMergeSortShuffleHandle和BaseShuffleHandle,两种ShuffleHandle对应的数据写入类为BypassMergeSortShuffleWriter和SortShuffleWriter,这里以SortShuffleWriter为例进行梳理。

  SortShuffleWriter写数据由下面的write方法实现,首先需要通过ShuffleBlockResolver获取到数据文件。

   /** Write a bunch of records to this task's output */
override def write(records: Iterator[Product2[K, V]]): Unit = {
  ...
// Don't bother including the time to open the merged output file in the shuffle write time,
// because it just opens a single file, so is typically too fast to measure accurately
// (see SPARK-3570).
val output = shuffleBlockResolver.getDataFile(dep.shuffleId, mapId)
   ...
  val tmp = Utils.tempFileWith(output)
  try {
    val blockId = ShuffleBlockId(dep.shuffleId, mapId, IndexShuffleBlockResolver.NOOP_REDUCE_ID)
    val partitionLengths = sorter.writePartitionedFile(blockId, tmp)
    shuffleBlockResolver.writeIndexFileAndCommit(dep.shuffleId, mapId, partitionLengths, tmp)

  具体调用ShuffleBlockResolver子类IndexShuffleBlockResolver中的getDataFile方法,此处可以看到实际是通过blockManager获取的数据。

def getDataFile(shuffleId: Int, mapId: Int): File = {
blockManager.diskBlockManager.getFile(ShuffleDataBlockId(shuffleId, mapId, NOOP_REDUCE_ID))
}

  然后生成ShuffleBlockId,通过ExternalSorter的writePartitionedFile方法最终写出数据。writePartitionedFile实质调用的仍是blockManager中的写方法。所以Shffle写数据底层是由blockManager实现。

 def writePartitionedFile(
blockId: BlockId,
outputFile: File): Array[Long] = { // Track location of each range in the output file
val lengths = new Array[Long](numPartitions)
val writer = blockManager.getDiskWriter(blockId, outputFile, serInstance, fileBufferSize,
context.taskMetrics().shuffleWriteMetrics)

3.2. Shuffle读数据

  Shuffle读数据是从SortShuffleManager的getReader获取一个数据阅读器,getReader方法中创建了BlockStoreShuffleReader实例。、

  BlockStoreShuffleReader的read方法首先实例化ShuffleBlockFetcherIterator。ShuffleBlockFetcherIterator中的成员blockManager管理内存和磁盘数据上的读写。

  * ShuffleBlockFetcherIterator中splitLocalRemoteBlocks划分本地和远程的blocks,Utils.randomize(remoteRequests)把远程请求通过随机的方式添加到队列中,fetchUpToMaxBytes发送远程请求获取blocks,fetchLocalBlocks获取本地的blocks。

Spark的Shuffle的更多相关文章

  1. 【Spark】Spark的Shuffle机制

    MapReduce中的Shuffle 在MapReduce框架中,shuffle是连接Map和Reduce之间的桥梁,Map的输出要用到Reduce中必须经过shuffle这个环节,shuffle的性 ...

  2. 【Spark篇】---Spark中Shuffle文件的寻址

    一.前述 Spark中Shuffle文件的寻址是一个文件底层的管理机制,所以还是有必要了解一下的. 二.架构图 三.基本概念: 1) MapOutputTracker MapOutputTracker ...

  3. 【Spark篇】---Spark中Shuffle机制,SparkShuffle和SortShuffle

    一.前述 Spark中Shuffle的机制可以分为HashShuffle,SortShuffle. SparkShuffle概念 reduceByKey会将上一个RDD中的每一个key对应的所有val ...

  4. Spark 的 Shuffle过程介绍`

    Spark的Shuffle过程介绍 Shuffle Writer Spark丰富了任务类型,有些任务之间数据流转不需要通过Shuffle,但是有些任务之间还是需要通过Shuffle来传递数据,比如wi ...

  5. 研究一下Spark Hash Shuffle 和 SortShuffle 原理机制

    研究一下Spark Hash Shuffle 和 SortShuffle 原理机制研究一下Spark Hash Shuffle 和 SortShuffle 原理机制研究一下Spark Hash Shu ...

  6. 剖析Hadoop和Spark的Shuffle过程差异

    一.前言 对于基于MapReduce编程范式的分布式计算来说,本质上而言,就是在计算数据的交.并.差.聚合.排序等过程.而分布式计算分而治之的思想,让每个节点只计算部分数据,也就是只处理一个分片,那么 ...

  7. [Spark]What's the difference between spark.sql.shuffle.partitions and spark.default.parallelism?

    From the answer here, spark.sql.shuffle.partitions configures the number of partitions that are used ...

  8. spark的shuffle机制

    对于大数据计算框架而言,Shuffle阶段的设计优劣是决定性能好坏的关键因素之一.本文将介绍目前Spark的shuffle实现,并将之与MapReduce进行简单对比.本文的介绍顺序是:shuffle ...

  9. 剖析Hadoop和Spark的Shuffle过程差异(一)

    一.前言 对于基于MapReduce编程范式的分布式计算来说,本质上而言,就是在计算数据的交.并.差.聚合.排序等过程.而分布式计算分而治之的思想,让每个节点只计算部分数据,也就是只处理一个分片,那么 ...

  10. 详细探究Spark的shuffle实现

    Background 在MapReduce框架中,shuffle是连接Map和Reduce之间的桥梁,Map的输出要用到Reduce中必须经过shuffle这个环 节,shuffle的性能高低直接影响 ...

随机推荐

  1. yum命令配置及使用说明和常见问题处理

    一. 重装yum 1. 执行如下命令,卸载yum命令程序 rpm -qa |grep yum |xargs rpm -e --nodeps 2. 依次执行如下命令,下载yum.yum-plugin-f ...

  2. MySQL问题:Access denied for user 'mysql'@'localhost'

    deep@deep-PC:~$ sudo mysql -uroot -p mysql> update mysql.user set authentication_string=PASSWORD( ...

  3. zabbix案例实例

    1 案例1:实现Zabbix报警功能 1.1 问题 沿用第5天Zabbix练习,使用Zabbix实现报警功能,实现以下目标: 监控Linux服务器系统账户 创建Media,设置邮件服务器及收件人邮箱 ...

  4. 通过bat批处理程序如何实现在多个txt文件后面加上相同的一行文字

    通过bat批处理程序如何实现在多个txt文件后面加上相同的一行文字 set/p a=输入要增加的文字 for /f "delims=" %%i in ('dir /b *.txt' ...

  5. Note 1: Good

    Note 1: Good 1. The collection of Linkun's [1]: 1.1coolJoy says "cool" when he heard that ...

  6. 通用 spring cloud 微服务模板

    说明文档 功能 1. 基于映射数据库一键生成 spring cloud 微服务 2. 通用 Controller ,无需编写代码即可完成基于数据库的服务 3. 动态多条件 CRUD + 分页 使用说明 ...

  7. 【ARM-Linux开发】gstreamer教程及在DM3730上的应用

    感谢原文作者:goalie高义http://blog.csdn.net/goalietech/article/details/24887955 1 Gstreamer基本概念 GStreamer 是一 ...

  8. mongodb的安装部署-备份

    1.安装部署 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.4.10.tgz tar -zxf mongodb-linux- ...

  9. Windows下直接双击可执行的jar

    如果没有设置,那么就是用命令行: jar处在文件夹路径下打开命令行:java -jar xxx.jar 总的来说是有点不方便 首先默认打开jar程序得是相同jdk的java.exe 然后是一闪而过 下 ...

  10. Linux中 cmake-3.x 编译安装以及man page添加

    首先回顾一下 cmake-2.x 的编译安装. ================ cmake-2.x编译安装说明 ================编译安装的命令: ./bootstrap --pref ...