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. HDU1003 最大连续子序列

    Max Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  2. ECS适合你吗?

    实体组件系统处于预览状态.不建议用于生产. 目前有两个很好的理由使用它. 你想试验 这是令人兴奋的新技术,并且大规模性能提升的承诺正在引诱.试试看.给我们您的反馈.我们很乐意在论坛上与您交谈. 您正在 ...

  3. 最新 前程无忧java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿.前程无忧等10家互联网公司的校招Offer,因为某些自身原因最终选择了前程无忧.6.7月主要是做系统复习.项目复盘.Leet ...

  4. 转换函数conversion function

    类转换分为两个角度 转换自身为其他类型 把其他类型转换为自身 Example: 这里我们可以将b转换为class xxx 的类型(方式2),也可以将me转换为double,然后再讲结果转换为doubl ...

  5. Python时间日期格式化之time与datetime模块

    1 引言 在实际开发过程中,我们经常会用到日期或者时间,那么在Python中我们怎么获取时间,以及如何将时间转换为我们需要的格式呢?在之前的开发中,也曾遇到time.datetime等模块下的不同函数 ...

  6. Java编程思想(四)初始化和清除

    4.1用构建器自动初始化 若某个类中有一个构建器,那么在创建对象时,Java会自动调用哪个构建器    在Java中构建器的名字必须与类名相同,这样可以保证这样一个方法惠子初始化期间自动调用: 利用构 ...

  7. JDBC:数据库连接技术

    JDBC :带它再爱你一次 (一) JDBC 入门 (1) 概述 Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问 ...

  8. [转帖]Linux-Windows 端口转发 netsh 还有 rinetd

    Linux-Windows 端口转发 https://www.cnblogs.com/operationhome/p/11284559.html 之前自己学习过 netsh 也曾经用过frp 这次学习 ...

  9. postgresSQL常用命令

    1.createdb 数据库名称  产生数据库2.dropdb  数据库名称  删除数据库 3.CREATE USER 用户名称  创建用户4.drop User 用户名称  删除用户 5.SELEC ...

  10. System memory 259522560 must be at least 4.718592

    [学习笔记] /*没有下面的话, 会报一个错误,java.lang.IllegalArgumentException: System memory 259522560 must be at least ...