堆内和堆外内存规划
1.堆内内存:由-executor-memory配置,executor内所有并发任务共享
序列化:将对象转换为二进制字节流,本质上可以理解为将非连续空间的链式存储转化为连续空间或块存储
2.堆外内存:由spark.memory.offHeap.size配置,优化内存的使用,提高shuffle时排序效率,存储经过序列化的二进制数据,默认关闭
3.内存管理接口:MemoryManager(静态内存管理(1.6)—>统一内存管理)
内存空间分配
1.静态内存管理
storage:缓存RDD数据和broadcast数据
可用存储内存 = systemMaxMemory * spark.storage.memoryFraction * spark.storage.safety
execution:缓存shuffle过程中中间数据
可用执行内存 = systemMaxMemory * spark.shuffle.memoryFraction * spark.shuffle.safety
other:用户代码执行区域
2.统一内存管理
Storage和Execution共享同一块空间,动态占用对方空闲区域
规则:
- storage和execution大小由spark.storage.storageFraction配置(0.6)
- 双方空间都不足,存储到磁盘;有一方空余,借用对方空间
- execution被对方占用,可让对方归还空间,将该部分转存到硬盘
- storage被占用,无法归还,shuffle过程较为复杂
存储内存管理
1.RDD的持久化机制
RDD:spark最基本的数据抽象,是只读的分区记录的集合—从稳定物理存储的数据集创建或已有的RDD转换。
RDD之间的转换会形成依赖关系,构成血统(Lineage),保证每个RDD可被恢复。
task启动之初读取某一分区,先判断是否持久化,无则检查checkpoint或按lineage重新计算。
持久化:persist或cache,cache默认为MEMORY_ONLY,由storage模块负责
checkpoint:持久化不能保证数据完全不丢失,可以将DAG中重要的数据存储到高可用的地方(HDFS)
2.RDD缓存的过程
RDD在缓存到storage内存之前,通过iterator获取分区中的数据项(record),record逻辑上占用JVM堆内内存的other空间,同一 partition的不同record空间不连续
RDD缓存到storage,partition转block,存储空间连续,该过程称为“展开”(unroll)
3.淘汰和落盘
新的block需要缓存到storage,但空间不足,对LinkedHashMap中旧block进行淘汰(Eviction)
淘汰的block如果可以存储到磁盘,则进行落盘(drop),否则删除
淘汰规则:
- 被淘汰的旧block要与新block的memorymode相同,同属于堆内或堆外内存
- 新旧block不属于同一个RDD
- 旧block所属RDD不处于被读状态,避免一致性问题
- 遍历LinkedHashMap中block,采用最近最少使用(LRU)顺序淘汰