Operator细粒度资源管理(FLink FLIP-53翻译)

本文详细介绍了Flink在FLIP-53中提出的细粒度Operator资源管理,旨在优化任务资源使用,提高效率。内容涵盖了目的、范围、公共接口、修改建议、内存配额策略、实施步骤以及后续计划,着重讨论了Operator如何基于比例获取托管内存资源,以适应流处理和批处理场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目的

目前(Flink 1.9), Flink采用粗粒度的资源管理方法,task根据job的最大并行度,部署到尽可能多的预定义slot中,而不管每个task/operator可以使用多少资源。
上述方法易于设置,但是不能最佳的利用资源和性能。

  • 任务可能具有不同的并行度(parallelisms),因此不是所有的slot都包含完整的任务管道(task pipeline)。对于任务较少的slot,为整个pipeline预定义的slot资源可能会造成浪费。
  • 在资源使用方面(堆、网络、托管内存等),很难将slot提供的资源与task需求保持一致。

我们计划实现细粒度资源管理,在tasks资源需求已知、tasks资源需求未知(即可调整),这两种情况下,优化资源的使用。

我们计划改进Flink的资源管理机制,使得:

  • 流处理和批处理模式下,均能很好的工作
  • 已指定或未知task需要的资源,均能很好的工作

当前FLIP主要关注细粒度资源管理的一个方面:Operator资源管理

  • 我们计划基于比例来管理内存配额,以统一以下内存管理场景:Operator指定了资源需求、Operator未指定资源需求
  • 我们计划将slot共享组缩小到pipelined regions单元,在不同的pipelined regions中,task可以并发运行,使用这种方法,对于tasks/Operator在同一个slot中共享内存,我们可以得到合理但又不太保守的比例。

范围

  • 当前FLIP提出的方法,应该只适用于Blink planner提供的DataStream API和SQL/Table API作业(blink planner内部包括无界流处理和有界批处理job)。当前FLIP不能作用于DataSet API(批处理job)
    • 对于Dataset Job,已经有一些基于比例的方法实现(在TaskConfig和ChainedDriver中),我们不会对这些已有的方法做任何更改
    • 本FLIP假定Job的Operators具有已知的资源请求配置,这些资源需求配置,已经在 PhysicalTransformations类中ResourceSpecs里描述。
      • 本FLIP不会讨论怎样设置一个Job中,Operators的资源需求配置
      • 当前状态(包括Flink 1.10开发计划),如何设置Job Opererators的资源需求的可以描述如下:
        • SQL/Table API–Blink optimizer(优化器)可以根据用户的configuration(配置),设置Operator资源(默认:未知)
        • DataStream API–目前没有设置Operator资源的方法/接口。可以在以后添加
        • DataSet API–已经有用户接口,用于设置Operator资源

公共接口

  • ResourceSpec对象
  • 引入一个新的配置“taskmanager.defaultSlotResourceFraction”,虽然不赞成这种方式,但是与"taskmanager.numberOfTaskSlots"配置保持兼容

修改建议

通用的Workflow(工作流)

细粒度Operator资源管理前提下,一个通用的workflow实现方式如下:

  • 未知的(默认)或指定的(例如blink planner)PhysicalTransformations#ResourceSpecs对象,它们描述了transformation的资源需求。
  • 当生成JobGraph时,StreamingJobGraphGenerator 为Operator计算内存比例(内存来自slot托管内存),将配置写入StreamConfigs
  • 当调度时,Operator的ResourceSpecs 会被转换为Tasks的ResourceProfiles (ResourceSpecs中 chained operators + network 总内存)。根据ResourceProfiles将task(任务)部署到 TMs的slot中
  • 在TMs中开始任务时,每个Operator获取slot托管内存的分数,该分数要么是原始请求的绝对值,要么是未知需求的公平份额。

Operator资源需求

Task可以消费TaskExecutor以下资源

  • Cpu Core
  • 用户堆上内存(Task Heap Memory in FLIP-49)
  • 用户堆外内存((Task Off-Heap Memory in FLIP-49)
  • 托管的堆上内存(On-Heap Managed Memory in FLIP-49)
  • 托管的堆外内存((Off-Heap Managed Memory in FLIP-49)
  • 为网络分配的内存((Network Memory in FLIP-49)
  • 扩展资源(GPU、FPFA、etc)

Operator在ResourceSpec中声明自己的资源需求,ResourceSpec应该包括所有资源维度,除了Network Memory(网络内存可以在运行时,从执行拓扑图中派生)。在这些维度之间,只要Operator声明了ResourceSpec,CPU core和用户堆上内存(unmanaged on-heap memory,所有Operator可以消费)就是必须使用的。而其他维度是可选的,如果没有显式指定,则默认设置为0。

如果Operator没有指定任何资源(ResourceSpec),它们的资源需求在默认情况下是未知的,这将由运行时决定它们可以消耗多少资源。

对于第一个版本,我们不支持在同一作业中,指定不同Operator不同资源需求(一部分Operator具有特定资源需求,一部分Operator没有指定)。同一作业的所有Operator都应该指定它们的资源需求,或者一个都不指定。StreamingJobGraphGenerator应该进行检查,并在编译阶段检测到混合资源需时,抛出错误。

托管内存申请

基于比例的配额策略

Operator不应在managed memory(托管内存上),获取绝对的内存配额。相反,应该应用一个相对的配额,以统一具有指定和未知的Operator资源需求。
在编译阶段,StreamingJobGraphGenerator应该为每个操作符设置两个比例。

  • fracManagedMemOnHeap - Operator在slot中,应该使用的on-heap managed memory(堆上托管内存)比例
  • fracManagedMemOffHeap - Operator在slot中,应该使用的off-heap managed memory(堆外托管内存)比例

比例的计算方式如下:

  • 有特定资源需求的Operator
    fracManagedMemOnHeap = opOnHeapManagedMem / slotSharingGroupOnHeapManagedMem
    fracManagedMemOffHeap = opOffHeapManagedMem / slotSharingGroupOffHeapManagedMem

  • 资源需求未知的Operator
    fracManagedMemOnHeap = 1 / numOpsUseOnHeapManagedMemoryInSlotSharingGroup
    fracManagedMemOffHeap = 1 / numOpsUseOffHeapManagedMemoryInSlotSharingGroup

  • runtime运行时还可以公开接口,以支持为具有不同权重的Operator设置比例。

当Task(任务)部署到TaskExecutor(任务执行程序)时,Operator应该将他们的内存使用比例,注册到MemoryManager。这一切发生在消费内存之前。注册应该根据比例返回绝对配额。通过这种方式,Operator可以根据其配额使用托管内存,并假设内存是可以保证的,或者让内存管理器(MemoryManager)限制它的内存消耗,并让Operator认识到,分配新内存不一定总是成功

Slot Sharing
在这里插入图片描述
在编译阶段,StreamingJobGraphGenerator首先标识JobGraph中的pipelined region,流水线区域(pipelined region)被定义为JobGraph中由pipelined edge(流水线顶点)相连的顶点子集,这些顶点总是被安排在一起。否则,当下游任务由于缺乏资源而无法调度,而上游任务由于没有下游任务读取输出而无法完成释放资源时,可能会出现死锁。
StreamingJobGraphGenerator将不同pipelined region的任务设置到不同的slot sharing groups中。这样,当StreamingJobGraphGenerator为Operator设置相对的托管内存配额时,它将只考虑可能同时运行的Operator来计算比例。这提高了bounded batch jobs(有限批处理作业)的资源利用率,在这些作业中,通常不是所有任务都同时运行。
对于使用DataStream API编写的作业,有一些公开给用户的接口允许用户显式地为Operator设置slot sharing group。在这种情况下,应该尊重用户的设置。
保持良好的特性也很重要,即无论作业图拓扑如何,Flink都需要与执行作业的最大任务并行性一样多的slot。对于批处理作业,可以很好地实现这一点,在批处理作业中,不同的pipelined region总是可以顺序运行,复用相同的插槽。然而,当流处理作业(Streaming Job)中包含多个连接组件(connected component)时,情况变得糟糕。虽然不同连接组件中的任务属于不同的流水线区域(pipelined region),但是所有连接组件中的任务都需要并发运行。因此,连接组件(connected component)的最大并行度之和,就是运行job所需的最少slot。
在这里插入图片描述
为了解决这个问题,我们需要能够将不同的连接组件(connected component)放到同一个槽共享组(slot sharing group)中,进行流处理作业(streaming jobs),另外,也可以将它们保存在不同的slot sharing group中,以避免大量slot中,任务不需要并发调度的情况。我们需要一个参数scheduleAllSourcesTogether来指示是否在相同的 pipelined region中标识所有的Source(想象一个虚拟Source连接到所有的真实源),并将其以不同的方式传递到StreamingJobGraphGenerator中,以用于流作业和批作业。
在调度阶段,我们总是为每个插槽共享组为每个并行管道请求一个插槽。

  • 对于具有指定资源需求的任务,我们将slot sharing group中所有任务的资源需求相加,并使用资源总和请求一个slot。
  • 对于具有未知资源需求的任务,我们请求一个具有默认资源的slot。

实施步骤

步骤1. 在ExecutionConfig中引入选项allSourcesInSamePipelinedRegion

  • 在ExecutionConfig中引入选项allSourcesInSamePipelinedRegion
  • 默认设置为true
  • Blink planner将SQL/Table API绑定的批处理作业设置为false
    此步骤不应引入任何行为更改。

步骤2. 根据pipelined region设置slot sharing group

在编译时,StreamingJobGraphGenerator为Operator设置slot sharing group 。

  • 识别pipelined region,同时参考allSourcesInSamePipelinedRegion
  • 根据pipelined region设置slot sharing group
    • 默认情况下,每个pipelined region应该进入一个单独的slot sharing group
    • 如果用户将多个pipelined region中的Operator设置为同一 slot sharing group,则应尊重这一点
      此步骤不应该引入任何行为更改,因为后面的pipelined region调度,可以复用前面已经调度的pipelined region。

步骤3. 在StreamConfig中引入托管内存比例

在StreamConfig中引入fracManagedMemOnHeap和fracManagedMemOffHeap,可由StreamingJobGraphGenerator设置,可以在运行时runtime中,由Operator使用。
此步骤不应引入任何行为更改。

步骤4. 根据slot sharing group设置托管内存比例

  • 对于具有指定ResourceSpecs的Operator,根据Operator ResourceSpecs,来计算比例
  • 对于未知ResourceSpecs的Operator,根据使用托管内存的Operator数量,来计算比例
    此步骤不应引入任何行为更改。

步骤5. Operator根据比例来决定分配多少托管内存

*Operator使用MemoryManager#computeNumberOfPages来获取memory segment(根据返回值)。

  • Operator使用MemoryManager#computeMemorySize来保留内存(根据返回值)。
    此步骤将激活新的基于比例的托管内存分配策略。

兼容性、弃用和迁移计划

此FLIP应该与以前的版本兼容。

测试计划

  • 我们需要更新现有的并添加新的集成测试,以验证新的细粒度资源管理行为。
  • 如果上述集成测试失败,其他常规集成和端到端测试也会失败。

被拒绝的方案

编译时设置 slot sharing group的另一种方法是,将具有指定资源需求的任务,设置为单独的slot sharing group(托管组中的任务除外),将具有未知资源需求的任务,设置为相同的slot sharing group。拒绝它是因为,它将任务从相同的pipelined region分离到不同的 slot sharing group,这可能会导致资源死锁的情况。
另一种方案,在调度阶段为Operator设置相对托管内存配额,同时了解哪些任务实际调度到相同的slot中。它被拒绝,因为即使我们在调度时设置了配额,也不能保证将来不会将任何任务部署到相同的槽中,而且动态更新比例要求操作符的内存使用是可撤销的。

后续

Operator托管内存超额分配和撤销

对于第一个版本,我们不允许Operator使用超过其配额的托管内存。
将来,我们希望允许Operator机会性的利用task executor中可用托管内存。Operator就可以申请超过配额的托管内存,只要task executor中有足够的可用托管内存,并且超出的配额在另一个任务需要时,可以撤销。

参考

[1] FLIP-49: Unified Memory Configuration for TaskExecutors

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值