再次深入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 Memory
和Storage Memory
, 同时Spark
应用运行时还有一块内存称为保留内存,需要注意的是这块内存并不是由Spark
来管理的,而是由JVM
的GC
来管理。
由Spark
管理的两大块内存,分别用于:
Execute memory
: 这部分内存被Spark
用于执行一些操作的存储,例如sort
,shuffe
,count
等等。Storage Memory
: 这部分内存主要用于缓存以及定义Spark
的广播变量。
而保留的内存,主要是供用户的Spark
应用中非Spark
代码定义的一些小的对象,由GC
来管理。
我们前面讨论过JVM GC
是完全不知道Spark
的内存管理模式的,因此它也就不会去协调Spark
管理的这部分内存和保留内存,大多数时候发生OOM
基本都是由于这个原因而产生的。
为了提升这个设计带来的问题,自从Spark 2.0
开始提出的新的设计概念,将Spark
的内存管理模式分为off-heap
模式和非off-heap
模式。
off-heap
模式
我们上面有讨论Spark
自己的内存管理主要是管理两个部分,即execute memory
和storage memory
, 由于GC
并不知道Spark
的这种内存管理方式,也就不存在协调这部分内存和其他内存之间的关系,有的时候可能会发生OOM
的问题,为了提升这部分,Spark
使用JVM
的特性,利用JVM
的API
- sun.misc.Unsafe
从JVM Heap
之外分配一部分内存专门给execute memory
和Storage 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 heap
30% 是一个通用的分配。
非off-heap
模式
这是默认的使用模式,在这个模式下,通过配置:spark.executor.memory
分配内存给executor
, spark.driver.memory
给driver
分配内存。 通过配置参数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
。