Spark内存管理之Execution内存管理

概述

以Shuffle的临时数据存储为例,介绍执行内存的使用。
Spark内存管理之堆内/堆外内存原理详解一文中,我们可以知道,无论是on-heap还是off-heap,都存在Execution内存,主要用于存放 Shuffle、Join、Sort、Aggregation 等计算过程中的临时数据

本文将主要从Shuffle的临时数据存储过程来说明Execution Memory如何发挥效用。

1. 多任务间的分配

Executor内运行的任务同样共享执行内存Spark用一个HashMap结构保存了任务到内存耗费的映射

  • 每个任务可占用的执行内存大小的范围为1/2N ~ 1/N,其中N为当前Executor内正在运行的任务的个数。
  • 每个任务在启动之时,要向MemoryManager请求申请最少为1/2N的执行内存,如果不能被满足要求则该任务被阻塞,直到有其他任务释放了足够的执行内存,该任务才可以被唤醒

2. Shuffle的内存占用

Shuffle存在Map端的Shufle与Reduce端的Shuffle:

  • Map端的Shufle:Map的输出结果结果首先被写入缓存(执行内存),当缓存满时,就启动溢写操作,把缓存中的数据写入磁盘文件,并清空缓存。当启动溢写操作时,首先需要吧缓存中的数据分区,然后对每个分区的数据进行排序(sort)和合并(combine),之后在写入磁盘文件。每次溢写操作会生成一个新的磁盘文件,随着Map任务的执行,磁盘中就会生成多个溢写文件。在Map任务全部完成之前,这些溢写文件会被归并(merge)成一个大的磁盘文件,然后通知相应的Reduce任务来获取属于自己处理的数据。
  • Reduce端的Shuffle:Reduce任务从Map端的不同Map机器领回属于自己处理的那部分数据,这些数据首先被放入缓存中,如果缓存被占满,会向Map端那样被溢写到磁盘中。多个经过排序、归并的溢写文件形成一个大文件(磁盘),然后将这若干个大文件,会交给Reduce处理。

从上面可以看出,执行内存需要用来存储任务在执行Shuffle时,shuffle的临时数据占用的内存。它不会被持久化在执行内存中。之所以说是临时,是因为实际上,最终数据都是写到磁盘的。Shuffle是按照一定规则对RDD数据重新分区的过程,我们来看Shuffle的Write和Read两阶段对执行内存的使用。

2.1 Shuffle Write对内存的使用

  • 若在map端选择普通的排序方式,那么map的输出结果会采用ExternalSorter进行外排,并写入内存缓存,主要占用堆内执行空间。
  • 若在map端选择Tungsten的排序方式,则采用ShuffleExternalSorter直接对以序列化形式存储的数据排序,在内存中存储数据时可以占用堆外或堆内执行空间,取决于用户是否开启了堆外内存以及堆外执行内存是否足够。

2.2 Shuffle Read对内存的使用

  • 在对reduce端的数据进行聚合时,会将从map端(磁盘/缓存)领回的数据交给Aggregator处理,同样写入内存缓存中,占用堆内执行空间。
  • 如果需要进行最终结果排序,则要再次将数据交给ExternalSorter处理,占用堆内执行空间。

2.3 溢写:处理shuffle执行内存不足

ExternalSorterAggregator中,Spark会使用一种叫AppendOnlyMap的哈希表在堆内执行内存中存储数据。但在Shuffle过程中所有数据并不能都保存到该哈希表中,对这个哈希表占用的内存会进行周期性地采样估算,当其大到一定程度,无法再从MemoryManager申请到新的执行内存时,Spark就会将其全部内容存储到磁盘文件中,这个过程被称为溢写(Spill),溢存到磁盘的文件最后会被归并(Merge)成一个大的磁盘文件。

2.4 Tungsten介绍

Shuffle Write阶段中用到的Tungsten是Databricks公司提出的对Spark优化内存和CPU使用的计划,解决了一些JVM在性能上的限制和弊端。Spark会根据Shuffle的情况来自动选择是否采用Tungsten排序。Tungsten采用的页式内存管理机制建立在MemoryManager之上,即Tungsten对执行内存的使用进行了一步的抽象,这样在Shuffle过程中无需关心数据具体存储在堆内还是堆外每个内存页用一个MemoryBlock来定义,并用Object objlong offset这两个变量统一标识一个内存页在系统内存中的地址。堆内的MemoryBlock是以long型数组的形式分配的内存,其obj的值为是这个数组的对象引用,offset是long型数组的在JVM中的初始偏移地址,两者配合使用可以定位这个数组在堆内的绝对地址;堆外的MemoryBlock是直接申请到的内存块,其obj为null,offset是这个内存块在系统内存中的64位绝对地址。Spark用MemoryBlock巧妙地将堆内和堆外内存页统一抽象封装,并用页表(pageTable)管理每个Task申请到的内存页。

Tungsten页式管理下的所有内存用64位的逻辑地址表示,由页号和页内偏移量组成:

1. 页号:占13位,唯一标识一个内存页,Spark在申请内存页之前要先申请空闲页号。
2. 页内偏移量:占51位,是在使用内存页存储数据时,数据在页内的偏移地址。

有了统一的寻址方式,Spark可以用64位逻辑地址的指针定位到堆内或堆外的内存,整个Shuffle Write排序的过程只需要对指针进行排序,并且无需反序列化,整个过程非常高效,对于内存访问效率和CPU使用效率带来了明显的提升(参见Spark Tungsten-sort Based Shuffle 分析探索Spark Tungsten的秘密

总结

主要通过shuffle临时数据的缓存来讲解了执行内存的作用。
注意这和Shuffle数据的持久化不同,shuffle数据持久化是结果数据持久化,并且最终是写入到磁盘中。

致谢

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值