Spark的内存模型以及Spark Shuffle的原理

一. Spark内存模型

在这里插入图片描述
堆外内存

为了进一步优化内存的使用以及提高 Shuffle时排序的效率,Spark 引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,存储经过序列化的二进制数据。

堆外内存意味着把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机)。这样做的结果就是能保持一个较小的堆,以减少垃圾收集对应用的影响。

二. Spark Shuffle的原理

Spark Shuffle分为map阶段和reduce阶段,或者称之为ShuffleRead阶段和ShuffleWrite阶段,一次Shuffle,map过程和reduce过程都会由若干个task来执行

shuffle read的拉取过程是一边拉取一边进行聚合的。每个shuffle read task都会有一个自己的buffer缓冲,每次都只能拉取与buffer缓冲相同大小的数据,然后通过内存中的一个Map进行聚合等操作。聚合完一批数据后,再拉取下一批数据,并放到buffer缓冲中进行聚合操作。以此类推,直到最后将所有数据到拉取完,并得到最终的结果

shuffle write在将数据写入磁盘之前,会先将数据写入内存缓冲中,当内存缓冲填满之后,才会溢写到磁盘文件中去。

在Spark1.2以前默认使用的是Shuffle计算引擎是HashShuffle,因为HashShuffle会产生大量的磁盘小文件从而导致 新能下降,在spark1.2之后版本中,默认的HashShuffle被修改后才能SortShuffle

SortShuffle相当于HashShuffle来说,有一定的改进,主要是在于,每个Task在进行shuffle的时候,虽然会产生很多临时磁盘文件,但是最后会将临时的磁盘文件进行合并(merge)成一个磁盘文件,因此每个Task只有有一个文件,在下一个stage拉取数据的时候,只要根据索引就可以获取磁盘中的文件

SortShuffle运行机制主要分为两种,一种是普通机制,另外一种是bypass运行机制。当shuffle read读取task的数量小于等于 200(默认值),就会启动bypass机制

2.1 HashShuffleManager(已经淘汰的shuffle机制)

普通的HashShuffleManager是早期的Spark版(1.2之前)本中的默认的shuffle的机制,但是这种shuffle机制有很明显的缺陷,下面是hashShuffleManager的原理图

图中是有两个executor, 每个executor中有一个cpu core,所以每个executor中的并行度是1,在进行shuffle的过程,map端shuffle的数据会先写到bucket缓存中(32kb,每次都只能拉取与buffer缓冲相同大小的数据),当达到一定的大小时,会溢写到磁盘上,每个Map task会为每个reduce task产生一份数据,所以map端一共产生的小文件是就是 map task 数* reduce task数。

在这里插入图片描述
假设一共有100个map task 和 100 个reduce task, 那么产生的小文件个数时10000个小文件,这么多小文件带来很多问题:

  1. 写磁盘的小文件多,IO多
  2. reduce 端读取数据时建立的 连接多
  3. 占用内存多,频繁gc,gc时会对外停止工作, 还可能导致oom内存溢出 如果说一台机器是32核 双线程,那么它能够并行的maptask的数量是64个,如果有1000个reduce task,那么所占用的内存就会有的64 * 100 * 32kb是2个多G,非常占用内存
  4. 在拉取数据时,可能会跨节点连接,跨节点连接可能会由于网络不稳定,连接中断,很耗时

2.2 SortShuffleManager(当前使用最多的shuffle机制)

SortShuffleManager的运行机制主要分成两种,一种是普通运行机制,另一种是bypass运行机制。当shuffle read task的数量小于等于spark.shuffle.sort. bypassMergeThreshold参数的值时(默认为200),就会启用bypass机制。

ps:普通机制会引发排序
(要尽量避免触发普通机制,因为普通机制在内存中会进行写入磁盘文件(会产生大量磁盘IO)和排序,会产生大量磁盘文件)

数据会先写入一个内存数据结构中,此时根据不同的shuffle算子,可能选用不同的数据结构。

如果是reduceByKey这种聚合类的shuffle算子,那么会选用Map数据结构,一边通过Map进行聚合,一边写入内存;

如果是join这种普通的shuffle算子,那么会选用Array数据结构,直接写入内存。接着,每写一条数据进入内存数据结构之后,就会判断一下,是否达到了某个临界阈值。如果达到临界阈值的话,那么就会尝试将内存数据结构中的数据溢写到磁盘,然后清空内存数据结构。

在溢写到磁盘文件之前,会先根据key对内存数据结构中已有的数据进行排序。排序过后,会分批将数据写入磁盘文件。默认的batch数量是10000条,也就是说,排序好的数据,会以每批1万条数据的形式分批写入磁盘文件。写入磁盘文件是通过Java的BufferedOutputStream实现的。

BufferedOutputStream是Java的缓冲输出流,首先会将数据缓冲在内存中,当内存缓冲满溢之后再一次写入磁盘文件中,这样可以减少磁盘IO次数,提升性能。

一个task将所有数据写入内存数据结构的过程中,会发生多次磁盘溢写操作,也就会产生多个临时文件。最后会将之前所有的临时磁盘文件都进行合并,这就是merge过程,此时会将之前所有临时磁盘文件中的数据读取出来,然后依次写入最终的磁盘文件之中。

此外,由于一个task就只对应一个磁盘文件,也就意味着该task为下游stage的task准备的数据都在这一个文件中,因此还会单独写一份索引文件,其中标识了下游各个task的数据在文件中的start offset与end offset。

2.3 Spark Shuffle与MR Shuffle的区别
在这里插入图片描述
相关文章:
Spark的运行模式及 Standalone模式的运行流程
Spark 弹性分布数据集RDD的介绍
常见的Spark的调优方法

有收获?希望烙铁们来个三连击,让更多的同学看到这篇文章

1、烙铁们,关注我看完保证有所收获,不信你打我。

2、点个赞呗,可以让更多的人看到这篇文章,后续还会有很哇塞的产出。

本文章仅供学习及个人复习使用,如需转载请标明转载出处,如有错漏欢迎指出
务必注明来源(注明: 来源:csdn , 作者:-马什么梅-)

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一马什么梅一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值