Skip to main content

再次深入Databricks内存管理

分类:  Azure Databricks 标签:  #Azure #Databricks 发布于: 2023-06-18 19:52:08

我们之前有一篇文章就用户的案例提及了Databricks的内存管理,我们本章计划再次深入学习一下内存管理。虽然Databricks是基于Spark开发的平台,但是Databricks的部署和用户自己搭建的Spark集群在配置上还是有不一样的地方。

谁在管理内存

所有的Spark应用都是运行在JVM的进程内的,对于JVM来说,它主要是通过GC来管理内存,而GC对于Spark的内存使用模式是一无所知的,也就是说Spark需要自己管理内存。相对于GC, Spark管理的内存其模式相对固定,它管理的内存主要是被分成两大块,Execute MemoryStorage Memory, 同时Spark应用运行时还有一块内存称为保留内存,需要注意的是这块内存并不是由Spark来管理的,而是由JVMGC来管理。

Spark管理的两大块内存,分别用于:

  • Execute memory: 这部分内存被Spark用于执行一些操作的存储,例如sortshuffecount等等。
  • Storage Memory: 这部分内存主要用于缓存以及定义Spark的广播变量。

而保留的内存,主要是供用户的Spark应用中非Spark代码定义的一些小的对象,由GC来管理。

我们前面讨论过JVM GC是完全不知道Spark的内存管理模式的,因此它也就不会去协调Spark管理的这部分内存和保留内存,大多数时候发生OOM基本都是由于这个原因而产生的。

为了提升这个设计带来的问题,自从Spark 2.0开始提出的新的设计概念,将Spark的内存管理模式分为off-heap模式和非off-heap模式。

off-heap模式

我们上面有讨论Spark自己的内存管理主要是管理两个部分,即execute memorystorage memory, 由于GC并不知道Spark的这种内存管理方式,也就不存在协调这部分内存和其他内存之间的关系,有的时候可能会发生OOM的问题,为了提升这部分,Spark使用JVM的特性,利用JVMAPI - sun.misc.Unsafe 从JVM Heap之外分配一部分内存专门给execute memoryStorage Memory使用,而且这部分内存不是由GC来管理的,是需要Spark自己利用API - sun.misc.Unsafe自己来管理。这样就不会存在由于GC导致Spark应用的各种问题,这个就是off-heap的由来以及基本的原理,当然如果你想详细的了解什么sun.misc.Unsafe, 你可以尝试goolge一下,了解一下JVM native技术。

要启用off-heap模式只需要使用参数spark.memory.offHeap.enabled等于true来启用,使用参数spark.memory.offHeap.size来指定它的大小,需要注意的是jvm heap的大小加上spark.memory.offHeap.size不能超过分配给JVM 进程的内存的总大小。 同时针对spark.memory.offHeap.size也要设置较为合适的值,太小,会多次落盘,降低performance, 太大,jvm heap就小,容易oom, 根据经验一版设置spark.memory.offHeap.size设置为JVM进程分配到的内存的70%, 剩下的jvm heap30% 是一个通用的分配。

off-heap模式

这是默认的使用模式,在这个模式下,通过配置:spark.executor.memory分配内存给executorspark.driver.memorydriver分配内存。 通过配置参数spark.memory.fraction 分配保留内存和execute memory以及storage memory, 可以使用如下的公式表示:

|storageMemory| + |execution| < fraction * (|JVM_heap| - ReservedMemory)

spark.memory.fraction的默认值是0.6, 也就是40%用于保留内存。

同时我们还有另外一个参数spark.memory.storageFraction(默认值是0.5), 用于分配execute memory以及storage memory, 虽然这二者会由Spark的动态进行调整。

Databricks部署内存计算

针对于databricks, 可以使用公式:spark.executor.memory = (0.8 * (vm_memory_size * 0.97 - 4800M) ) 来计算
用公式:spark.driver.memory = (0.8 (vm_memory_size 0.97 - 4800M)) - conf.chauffeurMemorySizeMB 来计算。

这其中 0.3 (1 - 0.97 ) 用于操作系统的kernel内存。
4800M 用于节点上的服务进程等等占用的内存。
0.2 (1- 0.8) 主要用于容器的保留内存。

conf.chauffeureMemorySizeMB在databricks上通常是1024MB