Spark静态内存管理

发布者:上海IT外包来源:http://www.lanmon.net点击数:1197

Spark引入了从1.6开始的动态内存管理模式,即执行内存和存储内存可以相互占用。 Spark提供两种内存分配模式:静态内存管理和动态内存管理。本系列文章分析了这两种内存管理模式的优缺点和设计原则。主要分析和解释了spark1.6的静态内存管理。
第二个内存空间分配
在Spark最初采用的静态内存管理机制中,存储内存,执行内存和其他内存的大小在Spark应用程序运行期间是固定的,但是用户可以在启动之前配置应用程序,并且堆中的内存分配是如下图所示。节目:
技术专栏| Spark静态内存管理
默认情况下,如果要启用静态内存管理模式,spark内存管理将使用统一模式。将Spark.memory.useLegacyMode参数设置为true(默认值为false)。官方网站的相关配置如下:
技术专栏| Spark静态内存管理
调整此参数后,从SparkEnv.scala可以看出,如果为true,则内存管理会调用静态内存类(StaticMemoryManager)。相反,内存管理使用统一的内存管理类(UnifiedMemoryManager)。
技术专栏| Spark静态内存管理
三个执行内存
可用的执行内存
用于shuffle聚合内存,具体取决于连接,排序,聚合等过程中频繁IO所需的临时数据存储。
简单来说,在随机写入过程中,每个执行程序都会将数据写入执行程序的物理磁盘。下一阶段任务将进入一个阶段来提取它需要处理的数据,它将拉动它。执行处理(与MapReduce一样拉取合并数据)。此时,聚合数据结构(例如散列映射)用于提取数据以进行聚合。这部分内存称为执行内存。从getMaxExecutironMemory方法,分配给每个执行程序执行的内存是:Execution Memory=systemMaxMemory * memoryFraction(默认值为0.2)* safetyFraction(默认值为0.8),默认为executor最大可用内存* 0.16。
技术专栏| Spark静态内存管理
执行内存在运行时分配给JVM上运行的任务。这里的区别在于分配给每个任务的内存不是固定的,而是动态的。 Spark一旦启动就不会为任务分配固定大小的内存块,但允许任务占用JVM的所有执行内存。
每个JVM上的任务最多可以应用1/N的执行内存(N是由spark.executor.cores指定的活动任务数)。如果任务的应用程序未被批准,它将释放一部分内存,并在下次应用时请求较小部分的内存。
注意:为了防止过多的溢出(逐出)数据,只有当任务分配的内存达到执行内存的1 /(2N)时才会溢出。如果当前空闲内存未达到1 /(2N),则内存应用程序将被阻止,直到其他任务内存丢失为止。如果不限制此操作,假设当前存在占用大部分内存的任务,则新任务将始终将数据溢出到硬盘,这将导致更严重的I/O问题。
例如,执行程序首先启动任务A,并在任务B启动之前快速占用所有可用内存。 (在B开始之后)N变为2,任务B将阻塞直到任务A溢出,并且我可以获得1 /(2N)=1/4的执行存储器。一旦任务B获得四分之一的内存,A和B都可能被泄漏。
保留的内存
Spark有一个像SafetyFraction这样的参数来避免潜在的OOM。例如,在计算时,存在相对大量的预先未预期的数据,这将导致延长的计算时间或甚至OOM。 safetyFraction为存储和执行提供了额外的缓冲区,以防止此类数据发生偏移。这部分内存称为保留内存。四个存储内存
可用存储内存
这部分内存用作RDD的缓存(如调用缓存,持久等),节点之间传输的广播变量。
从StaticMemoryManager的单例对象中,您可以看到为每个执行程序分配给存储的内存:
StorageMemory=systemMaxMemory * storageMemoryFraction(默认值为0.6)* safetyFraction(默认值为0.9)=0.54,
这意味着默认情况下会分配执行程序最大可用内存的* 0.54。源代码如下:
技术专栏| Spark静态内存管理
保留的内存
与执行内存的保留部分相同。

展开是存储的一个特殊部分,默认为总内存的20%。
BlockManager是一个由spark本身实现的内部分布式文件系统。 BlockManager以迭代器的形式接受数据(可能来自本地或其他节点),数据被序列化和非序列化。需要注意以下两点:
a)迭代器在物理内存中不连续。如果后续的spark想要将数据加载到内存中,则需要将数据放入一个数组中(物理上是连续的)。
b)此外,需要扩展序列化数据。如果直接扩展序列化数据,则会导致OOM。因此,BlockManager将逐渐扩展迭代器并逐步检查内存中是否有足够的空间来扩展数据。在阵列中。
技术专栏| Spark静态内存管理
展开的优先级仍然相对较高。它使用的内存空间可以从存储中借用。如果存储中没有现有数据块,它甚至可以占用整个存储空间。如果存储中存在数据块,则可以丢弃最大内存的数据由spark.storage.unrollFraction控制。从图6中可以看出,此部分默认为存储的20%。
注意:此20%空间不是静态保留的,而是通过将数据块丢弃在内存中来分配的。如果展开失败,spark会将这部分数据驱逐到硬盘。
五其他部分
该内存用于程序本身运行所需的内存,以及用户定义的数据结构和创建的对象。此内存有以上两部分,默认为0.2。六个限制
spark的设计文档表明静态内存具有以下限制:
(1)所有应用程序都没有默认配置,开发人员通常需要为不同的应用程序配置不同的参数。例如,根据任务的执行逻辑,调整随机播放和存储内存比例以满足任务的需要。
(2)这要求开发人员对火花原理有很高的了解。
(3)不缓存数据的应用程序在运行时仅消耗少量可用内存,因为在默认内存配置中,存储使用60%的内存。
概念性补充
驱逐策略:在spark技术文档中,经常出现驱逐一词。驱逐不仅仅是一个字面上的驱逐。说到一边,我们通常把它称为内存计算框架。严格来说,spark并不是内存计算的新技术。无论运算符如cache还是persist,spark都在内存排列中,大多数都使用LRU策略(LRU可以说是一种算法,它也可以作为判断如何从Cache LRU中清除对象的原则是“最近最少使用”的原则。当缓存溢出时,最近最少使用的对象将从缓存中清除。也就是说,当存储器不足时,最远使用的存储器数据块将被驱逐。当逐出时,spark会将数据块驱逐到硬盘而不是简单地丢弃它。
无论是存储空间还是执行存储空间,当内存空间不足时,spark都会将数据驱逐到硬盘上。
因此,如果开发人员没有在内存分配中进行合理分配,无论存储或执行是否超出内存限制,spark都会将内存数据写入硬盘。在存储的情况下,甚至可以将所有存储器数据写入硬盘并丢弃它。这样做无疑会增加系统调用,I/O和重复计算的开销。在火花任务中已经发展了很多洗牌阶段的学生应该有同样的感觉。当shuffle内存不足时,从溢出到硬盘的数据将非常大,导致任务变慢,甚至导致任务的各种重试尝试失败。这种情况表明增加了随机存储器分数。如果资源是在纱线上安排的,建议通过spark.yarn.executor.memoryOverhead增加堆内存。有时它将调整为2g,3g,4g,直到任务成功。有关与火花相关的优化,请参阅spark系列中的后续文章。
IT外包
>
400-635-8089
立即
咨询
电话咨询
服务热线
400-635-8089
微信咨询
微信咨询
微信咨询
公众号
公众号
公众号
返回顶部