文章目录
- 目的:
- 公共接口
- 修改建议
- Memory Pools和配置方式
目的:
该提案目的是解决Flink 1.9 TaskExecutor内存配置的几个缺点。
(1)解决流、批配置差异大
目前,流和批作业TaskExecutor内存的配置各不相同。
- Streaming(流处理)
- 内存是隐式消耗的,要么在堆上由Memory State Backend后端消耗,要么在堆外由RocksDB消耗。
- 用户必须手动调整堆大小和手动选择后端(state backend)。
- 用户必须手动配置RocksDB,以使用足够的内存来实现良好的性能,但又不能超出预算。
- 内存消耗无法预测,包括on-heap(堆内)的memory后端,以及off-heap(堆外)的RocksDB后端
- Batch(批处理)
- 用户手动配置总内存大小,以及在Operator(算子)中使用堆上内存还是堆外内存。
- Flink将总内存的一部分保留为managed memory托管内存。它自动调整heap大小和“max direct memory”参数,以适应堆内、堆外内存的管理。
- Flink为Operators申请托管的Memory Segments。并保证不会超过剩余的Memory Segments(内存段)。
(2)解决Streaming方式RocksDB配置复杂
- 用户必须手动减少JVM堆大小,或者将Flink设置为使用堆外内存。
- 用户必须手动配置RocksDB内存。
- 用户无法尽可能多地使用可用内存,因为RocksDB内存大小必须配置得足够低,以确保不会超出内存预算。
(3)去掉复杂、不确定、难以理解的配置
- 在配置container、进程的内存大小时,有一些“magic魔法”。其中一些是不容易推理的,例如yarn container“保留的内存”。
- 配置一个像RocksDB这样的堆外状态后端意味着要么将托管内存设置为堆外,要么调整截止比,从而减少为JVM堆提供的内存。
- TaskExecutor依赖于瞬时的JVM内存使用来确定不同内存池的大小,首先触发GC,然后获得JVM空闲内存大小,这给不同内存池的大小带来了不确定性。
公共接口
TaskExecutor内存配置选项。以及向后兼容性
修改建议
统一流处理和批处理内存管理
基本思想是将状态后端使用的内存视为托管内存的一部分,并扩展MemoryManager(内存管理器),以便状态后端可以简单地从MemoryManager那里保留一定量的内存,但并不是必须从MemoryManager那里分配内存。
通过这种方式,用户能够不修改集群配置的情况下,切换流作业和批作业。
内存使用场景及特点
- 使用Memory/FsStateBackend的流作业(特点):
- JVM堆内存
- 由状态后端隐式申请内存
- 对整体的内存消耗没有控制
- 使用RocksDBStateBackend的流作业(特点):
- 堆外内存
- 由状态后端隐式申请内存
- 不能超过初始化期间配置的总内存大小
- 批处理作业(特点):
- 堆内存
- 从内存管理器显式分配
- 不能超过从内存管理器分配的总内存
统一显式和隐式内存申请
- Memory Consumer可以通过两种方式获取内存
- 以MemorySegment的形式,显式地从MemoryManager中获取。
- 从MemoryManager中预先保留,再使用,在这种情况下应该返回“使用最多X个字节”,并由Memory Consumer自己隐式地申请内存。
将托管的堆上内存池和堆外内存池分离
当前(Flink 1.9),所有托管内存都以相同的类型分配,不管是在堆上还是堆外。这对于当前用例来说是很好的,在当前用例中,我们不需要在同一TaskExecutor中同时使用堆上和堆外托管内存。
在这次建议的设计中,state backend(状态后端)使用的内存也被认为是托管内存,这意味着在相同集群中