spark内存模型
内存模型
1、ExecutionMemory用来计算shuffles、joins、sorts和aggregations
2、Storage不是一个真的内存工具,它只是把内存作为他的LRU缓存,Storage默认情况下占executor.memory的54%,用于缓存和传播跨集群的内部数据
3、unroll部分内存: spark.storage.unrollFraction。当我们需要在内存展开数据块的时候使用,那么为什么需要展开呢?因为spark允许以序列化和非序列化两种方式存储数据,序列化后的数据无法直接使用,所以使用时必须要展开。该部分内存占用缓存的内存,所以如果需要内存用于展开数据时,如果这个时候内存不够,那么Spark LRU缓存中的数据会删除一些快。
4、MemoryManager有两种内存管理模型
StaticMemoryManager:一个硬性的边界在存储和执行内存中 通过静态给spark内存分区,阻止存储和执行互相借用内存,这种模式已过时,保留只是为了兼容性
UnifiedMemoryManager:联合内存的概念,最主要的改变是存储和运行的空间可以动态移动。需要注意的是执行比存储有更大的优先值。Execution 可以强制把 Storage 一部份空间挤掉。Excution 向 Storage 借空间有两种方式:第一种方式是 Storage 曾经向 Execution 借了空间,它缓存的数据可能是非常的多,当 Execution 需要空间时可以强制拿回来;第二种方式是 Execution Memory 不足 50% 的情况下,Storgae Memory 会很乐意地把剩馀空间借给 Execution。
5、User Memory:写 Spark 程序中产生的临时数据或者是自己维护的一些数据结构也需要给予它一部份的存储空间,你可以这么认为,这是程序运行时用户可以主导的空间,叫用户操作空间。它占用的空间是 (Java Heap - Reserved Memory) x 25% (默认是25%,可以有参数供调优),这样设计可以让用户操作时所需要的空间与系统框架运行时所需要的空间分离开。假设 Executor 有 4G 的大小,那么在默认情况下 User Memory 大小是:(4G - 300MB) x 25% = 949MB,也就是说一个 Stage 内部展开后 Task 的算子在运行时最大的大小不能够超过 949MB。例如工程师使用 mapPartition 等,一个 Task 内部所有算子使用的数据空间的大小如果大于 949MB 的话,那么就会出现 OOM。思考题:有 100个 Executors 每个 4G 大小,现在要处理 100G 的数据,假设这 100G 分配给 100个 Executors,每个 Executor 分配 1G 的数据,这 1G 的数据远远少于 4G Executor 内存的大小,为什么还会出现 OOM 的情况呢?那是因为在你的代码中(e.g.你写的应用程序算子)超过用户空间的限制 (e.g. 949MB),而不是 RDD 本身的数据超过了限制。
如果是你的计算比较复杂的情况,使用新型的内存管理 (Unified Memory Management) 会取得更好的效率,但是如果说计算的业务逻辑需要更大的缓存空间,此时使用老版本的固定内存管理 (StaticMemoryManagement) 效果会更好