本节解释如何根据用例设置内存,以及每种用例中哪些选项是重要的。
内存调优
1. 为standalone deployment配置内存
建议配置 Flink总内存(taskmanager.memory.flink.size or jobmanager.memory.flink.size),因为Flink总的进程内存不重要,因为JVM开销不受Flink或部署环境控制,在这种情况下,只需要考虑执行机器的物理资源。
2. 为container(即yarn或k8s部署)配置内存
在容器化(即yarn或k8s)部署时,建议配置Flink总进程内存(taskmanager.memory.process.size or jobmanager.memory.process.size)。该值对应于需要申请多大内存的container大小。假如配置的是Flink总内存(即 Total Flink memory不是 Total Process memory)则Flink会隐士的添加JVM内存组件然后再按添加后的值去请求一个container,很明显这不是我们想要的结果,不是很准确的展示我们想要分配的内存大小。
注意如果Flink或用户代码分配的非托管堆外(本地)内存超过了容器大小,作业可能会失败,因为部署环境可能会杀死有问题的容器。
3. 为state backends配置内存
这只和TaskManager相关。
部署Flink流处理应用时,使用的后端状态类型决定了集群的最佳内存配置。
- HashMap state backend
当运行无状态作业或使用HashMapStateBackend时,将托管内存(managed memory)设置为0。这将确保为JVM上的用户代码分配最大数量的堆内存。
- RocksDB state backend
EmbeddedRocksDBStateBackend 使用本地内存(native memory),默认情况下,RocksDB被设置为将本地内存分配限制在托管内存的大小范围内,因此,为你的状态保留足够的托管内存非常重要。如果你禁用了默认的RocksDB内存控制,如果RocksDB分配的内存超过了请求的容器大小(总进程内存)的限制,那么在容器化部署中,TaskManagers可能会被终止。
4. 为batch jobs配置内存
这只和TaskManager相关。
Flink的批处理操作符利用托管内存来提高运行效率。这样一来,某些操作可以直接在原始数据上执行,无需将其反序列化为Java对象。这意味着托管内存配置对应用程序的性能有实际影响。Flink会尝试为批处理作业分配和使用尽可能多的托管内存,但不会超出其配置的限制。这可以防止OutOfMemoryError,因为Flink能准确知道它需要利用多少内存。如果托管内存不足,Flink会优雅地将数据转存到磁盘。
问题处理
1. IllegalConfigurationException
如果你看到TaskExecutorProcessUtils或JobManagerProcessUtils抛出IllegalConfigurationException,这通常意味着存在无效的配置值(例如,负内存大小、大于1的fraction配置值等)或配置冲突。
2. OutOfMemoryError: Java heap space
该异常通常表示JVM堆太小。您可以尝试通过增加总内存来增加JVM堆大小。你也可以直接为taskmanager增加任务堆内存(Task Heap),或者为jobmanager增加JVM堆内存(JVM Heap)。
你也可以为taskmanager增加框架的堆内存(framework heap),但只有在确定Flink框架本身需要更多内存的情况下,才应该改变这个选项。
3. OutOfMemoryError: Direct buffer memory
该异常通常表示JVM直接内存限制过小或存在直接内存泄漏。检查用户代码或其他外部依赖项是否使用JVM直接内存,以及是否正确地使用了这些内存。您可以尝试通过调整直接堆外内存(direct off-heap memory)来增加其限制。
4. OutOfMemoryError: Metaspace
该异常通常表示JVM元空间限制配置过小。你可以尝试为TaskManager或JobManager增加JVM metaspace选项。
5. IOException: Insufficient number of network buffers
这只和TaskManager相关。
该异常通常表示配置的网络内存不够大。你可以尝试通过调整以下选项来增加网络内存:
taskmanager.memory.network.min
taskmanager.memory.network.max
taskmanager.memory.network.fraction
6. Container Memory Exceeded
如果Flink容器试图分配超出其请求大小的内存(Yarn或Kubernetes),通常表明Flink没有分配足够的本地内存。你可以通过使用外部监视系统或部署环境终止容器时的错误消息来观察这一点。
如果用户在JobManager进程中遇到这个问题,可以通过启用 jobmanager.memory.enable-jvm-direct-memory-limit 配置项来排除可能的JVM直接内存泄漏。
如果使用RocksDBStateBackend,而且内存控制被禁用,可以尝试增加TaskManger的托管内存;又或者使用了RocksDBStateBackend,内存控制启用而且在保存点或完整检查点期间,非堆内存增加,这可能是由于glibc内存分配器(参见glibc bug)造成的。你可以尝试为TaskManager添加环境变量MALLOC_ARENA_MAX=1。
或者直接增加JVM Overhead。