先说个我自己线上踩过的场景:集群里3台TaskManager,每台4个Slot,总容量12。我先后提了两个实时作业,一个并行度10,一个并行度2。按道理12个Slot装12个并行子任务是刚好够的,结果作业跑起来之后,一台TaskManager上堆了8个子任务,CPU被打到95%以上,GC频繁告警;另外两台一台只跑了2个子任务,一台跑了2个,网络和CPU都在那闲着。我盯了一眼Web UI上的TaskManager列表,第一反应是“调度器是不是串行了”。后来检查发现,问题不是串行,而是并行度不一致时,默认的Slot分配逻辑天然就会往“先到先得、尽量聚堆”的方向走。这篇文章就聊清楚这件事:Flink里面Balanced Tasks Scheduling到底在解决什么问题,并行度不一致时怎么让TaskManager被压得更均匀,以及我实际验证过的配置、代码和排查手段。
1. 场景复盘:并行度不一致,为什么TaskManager会“旱的旱死、涝的涝死”
1.1 一个典型的集群分配现场
先还原当时的分配过程。我提交的第一个作业并行度是10,提交的时候3台TaskManager全部空闲。Flink默认的Slot分配策略是SLOT_CONSTELLATION,也就是“星座模式”,它会把同一个作业需要的Slot尽量塞到尽量少的TaskManager上,目的是减少跨TaskManager的数据交换,提升本地性。所以并行度10的作业提上去之后,前4个Slot落在TM1,再4个落在TM2,最后2个落在TM3。这时候分布是TM1=4、TM2=4、TM3=2,还算能接受。
问题出在第二个并行度2的作业上。如果调度器知道TM3目前只占了2个Slot,它应该优先把新作业的2个Slot放到TM3,这样最终分布会变成TM1=4、TM2=4、TM3=4,非常理想。但实际并不是这样。默认的SLOT_CONSTELLATION策略在处理第二个作业时,会倾向于在这个作业自己的“已分配Slot集合”里找连续性,也就是说,它优先考虑把第二个作业的2个Slot放在同一个TaskManager上,以便这个作业内部的算子尽量本地化。于是它先看TM1,发现有空位,就把2个Slot都压给了TM1。最终结果就是TM1上跑了8个子任务,TM2和TM3各2个。
这个现象在并行度不整除Slot总数时特别容易触发。比如3台机器每台4 Slot,一个并行度10的作业加一个并行度2的作业,组合出来就是“8、2、2”;再比如5台机器每台3 Slot,并行度8加并行度4,分配结果往往是一台机器塞5个Slot,另外几台只有2个或3个。你可以理解为:调度器倾向于让“新来的”扎堆,而不是让“已有的”填平。
1.2 不均衡分配带来的四个连锁问题
第一是CPU倾斜。Flink的TaskManager在同一个JVM里跑多个Task,如果某个TaskManager上分配的并行子任务特别多,它的CPU和内存压力会明显高于其他节点。实时计算场景下,CPU超过85%之后,GC停顿时间开始不可控,处理延迟跟着抖动。
第二是网络和I/O倾斜。某些并行子任务如果恰好承担了高流量分区(比如keyBy之后某个key的数据量特别大),而它所在的TaskManager又没有多余的资源做缓冲,背压就会从该TaskManager传播到上游,整个拓扑的吞吐被拖到最低点。注意,这里即使Slot分布均匀,数据倾斜也可能导致单点热点,但Slot分布不均匀会放大这个热点的影响面,因为它把多个高负载子任务叠在了同一台机器上。
第三是故障恢复的代价不均匀。Flink做Task Failover时,如果一个TaskManager挂了,它上面所有Task都需要重新调度和恢复。如果这台机器上堆了8个并行子任务,重启恢复的时间和下游对齐成本就是别人的好几倍。极端情况下,恢复过程中又会因为资源不足触发二次分配,雪上加糕。
第四是资源碎片化。当Slot被不均匀占用后,集群里会剩余很多“零散Slot”。比如TM1满了、TM2剩2、TM3剩3,接下来你提交一个并行度4的作业,会面临两种选择:要么拆到两台上,跨机器传输增多;要么再等一个作业释放出一台完整的机器。无论哪种,资源利用率和作业性能都会打折扣。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 先搞懂Flink Slot分配的基本逻辑
2.1 Slot是什么,别把它当成固定容器
很多人把Slot理解成TaskManager上的一块固定大小的内存区域,这不太准确。Flink的Slot本质上是TaskManager资源池里的一个“调度凭证”,它决定了这个TaskManager最多能同时运行多少个并行子任务(准确说是多少个Slot Sharing Group实例)。Slot本身没有固定的内存划分,TaskManager的所有任务共享JVM堆内存和托管内存,Slot只是并发执行能力的上限控制。
一个Slot能跑多少个算子,取决于Slot Sharing Group的配置。默认情况下,整个作业的所有算子共享同一个group,所以一个Slot里会串起Source、KeyBy、Window、Sink等一条完整的pipeline。这也就意味着,给一个Slot分配一个subtask,实际上是把这一整条pipeline的一个实例放到这台TaskManager上。所以在判断TaskManager负载时,只看Slot数量是不够的,还得看每个Slot里跑的是轻量Filter还是重量级Window。
2.2 两种内置分配策略:SLOT_CONSTELLATION 和 SLOT_SPREAD
Flink调度器在给作业分配Slot时,有两个经典的策略选择,它们控制着“作业的Slot应该聚拢还是散开”。
SLOT_CONSTELLATION(默认):它优先把同一个作业的Slot分布到尽可能少的TaskManager上。策略执行时会维护一个候选TaskManager列表,优先选择当前已经为这个作业分配过Slot的TaskManager,只有当现有候选都满了,才扩展新的TaskManager。目标很明确:最大化本地性,减少TaskManager之间的数据交换。对于内部shuffle量大的作业(比如窗口聚合、大状态计算),这个策略通常更优。
SLOT_SPREAD:它把同一个作业的Slot尽量平均地分布到所有TaskManager上。每次需要新Slot时,它会选择至今为止分配比例最低、还有空位的TaskManager。这样可以让集群中的每台机器负载更均衡,适合那些外部I/O较多、shuffle较少的作业,或者集群中同时跑多个作业、希望避免单机热点的场景。
这两个策略可以通过参数cluster.evenly-spread-out-slots来切换,设置为true时启用SLOT_SPREAD。但从我实际测试来看,这个参数解决的是“同一个作业的Slot是否撒开”,它对“多个作业、不同并行度叠加后的全局均衡”处理得并不彻底。原因很简单:它是在单个作业维度做均衡,而不是站在整个集群维度统筹。
2.3 默认策略为什么面对不齐并行度会“偏科”
SLOT_CONSTELLATION在单个作业并行度较大时表现很好,因为它能把一个作业的pipeline尽量聚合在少数机器上。但一旦集群里同时存在多个并行度不一致的作业,问题就出现了:每个作业都以“自己内部尽量聚堆”为目标,结果就是每个作业都倾向抢占那些已经空闲、或者自己已经占过的机器,而不是去填平其他作业留下的“坑”。
举个例子,3台机器各4 Slot。作业A并行度6提上去,SLOT_CONSTELLATION会把它压在TM1和TM2上(TM1=4,TM2=2),TM3完全空闲。作业B并行度4再提上去,它不知道作业A在TM2只占了2个Slot,只会按照自己的“聚堆偏好”选择TM3(因为TM3完全空闲,最容易整组放进去)。最终分布变成TM1=4,TM2=2,TM3=4,看似比之前好了,但还不够均匀。如果作业B的并行度是6,它会把TM3占满后还剩2个,再回到TM2,最终变成TM1=4,TM2=4,TM3=4,刚好。但现实中的并行度组合不会每次都这么凑巧,所以需要一种站在全局视角的均衡分配思路。
这就是Balanced Tasks Scheduling要解决的核心问题:当集群中存在多个并行度不一致的作业时,如何让所有TaskManager上的Slot占用比例尽量接近,而不是让每个作业“各自为政”。
3. Balanced Tasks Scheduling核心思路与算法拆解
3.1 核心目标:从“谁先申请谁先得”变成“按容量比例分配”
我在参考资料和社区讨论里看到的Balanced Tasks Scheduling,本质上不是某一行配置,而是一套分配策略的组合。它的核心目标可以用一句话总结:让每个TaskManager上的slot占用率趋向于集群平均占用率。具体到调度的判断依据,不再是“这个作业在哪个TM上已经有过Slot”,而是“哪个TM当前的相对负载更低,优先把新的Slot分配给谁”。
相对负载的计算方式是:已分配Slot数 / TaskManager总Slot数。比如TM1是4/8=0.5,TM2是1/4=0.25,那么新Slot应该优先给TM2,因为它的占用率更低。注意这里不能只看绝对剩余Slot数。TM1虽然还剩4个空位,但它的总容量是8,继续往里塞会让它变成5/8甚至8/8;而TM2只有1/4,空间比例上更“饿”。
这套逻辑处理并行度不一致的场景时,比“按剩余空位数分配”更稳。原因是:如果A作业已经占了TM1的3个Slot,B作业申请4个Slot时,按剩余空位数算TM1还有1个空位,不会选它,于是B把4个Slot全堆到TM2上,导致TM2满负荷;但如果按占用率算,TM1占用率3/4,TM2占用率0/4,B会优先填TM2,填到2/4之后TM2占用率变成0.5,TM1还是0.75,继续填TM2,直到TM2的占用率追上TM1为止。这样最终TM1=3,TM2=4,尽管不是绝对平均,但比TM1=3、TM2=4、TM3=0的情况要均匀得多。
3.2 分配算法的主流程
如果让我用伪代码来描述一套能够落地的均衡分配流程,大概是这样:
text复制输入:
- 所有TaskManager集合 T,每个tm记录总Slot数totalSlots和已分配Slot数allocatedSlots
- 当前作业需要的所有Slot请求集合 R(每个请求对应一个SlotSharingGroup实例)
流程:
1. 计算集群总Slot数 totalClusterSlots = sum(tm.totalSlots for tm in T)
2. 计算集群平均占用率 avgLoad = sum(tm.allocatedSlots for tm in T) / totalClusterSlots
3. 对R中的每个Slot请求,按所在TaskManager的“当前占用率升序”排序:
- 占用率越低的TM,越优先被分配
4. 如果多个请求属于同一个SlotSharingGroup,优先把它们分到同一个TM:
- 该TM必须还有空余Slot
- 并且该TM上已经存在这个group的实例,或者该TM的占用率排序最靠前
5. 分配下一个Slot时,重新计算占用率,重新排序,再取当前最优TM
6. 如果某个TM的空闲Slot不足以容纳某个SlotSharingGroup的所有实例,则拆开到下一个最优TM
这个流程的关键点有三个:
- 每次分配后都要重新计算占用率并排序,而不是一开始排完序就固定不变。因为先分配的Slot会改变TM的负载,如果不更新排序,后面的分配可能还是堆到同一台机器上。
- SlotSharingGroup的实例尽量同机,但前提是同机的负载不超标。如果一个group有4个实例,而当前最优TM只剩2个空位,那就拆,不要硬塞。这跟SLOT_CONSTELLATION的区别在于:SLOT_CONSTELLATION优先保同机,再考虑负载;均衡策略优先保负载约束,在负载约束内尽量同机。
- 参与计算的占用率是全局视角,也就是把集群里所有作业占用的Slot都算进去,而不仅仅是当前作业的。这样才能真正做到多个作业并行度不一致时的全局均衡。
3.3 一个手算实例:并行度6 + 并行度2 如何均匀分摊到3个TaskManager
我拿最开始那个场景做个手算演示。3台TM,每台总Slot数4,初始全部空闲。
步骤一:提交作业A,并行度6,默认Slot Sharing Group,所以需要6个Slot。第一次分配状态是TM1=0/4、TM2=0/4、TM3=0/4,排序都相同,按顺序选TM1,分配第1个Slot,状态变成TM1=1/4、TM2=0/4、TM3=0/4。重新计算,TM2和TM3占用率最低,但是因为作业A要尽量同机,TM1已经有一个实例了,所以第2个Slot仍给TM1,状态变成TM1=2/4。同理第3、第4个Slot也进TM1,直到TM1=4/4。此时第5个Slot必须换机器,在TM2和TM3之间选一台,假设选TM2,状态TM2=1/4;第6个Slot接着给TM2,状态TM2=2/4。最终作业A的分布是TM1=4、TM2=2、TM3=0。
步骤二:提交作业B,并行度2。按照均衡策略,当前状态是TM1=4/4、TM2=2/4、TM3=0/4。先算占用率:TM1=1.0,TM2=0.5,TM3=0.0。作业B的第1个Slot应该选TM3,状态变成TM3=1/4。第2个Slot重新排序,当前占用率TM1=1.0、TM2=0.5、TM3=0.25,TM2最低,所以第2个Slot给TM2,状态变成TM2=3/4、TM3=1/4。最终全局分布是TM1=4、TM2=3、TM3=1。
对比一下默认策略的分布:TM1=4、TM2=2、TM3=2(实际上默认SLOT_CONSTELLATION可能会把作业B的2个Slot都堆到TM3或都堆到TM2,取决于当时候选列表顺序,最差情况甚至会是TM1=4、TM2=4、TM3=0)。均衡策略的结果虽然没有做到严格平均(严格平均应该是333),但至少避免了单台机器上堆8个Slot的极端情况。
如果继续提交作业C,并行度3,均衡策略会这样走:当前TM1=1.0、TM2=0.75、TM3=0.25。作业C的Slot按占用率从低到高分配,TM3拿2个到3/4,TM2拿1个到4/4,最终TM1=4、TM2=4、TM3=4,完美均匀。你会发现,只要持续用“全局占用率最低优先”的策略,集群最终会趋向于所有TM占用率一致,不管后面进来多少个并行度不一的作业。
4. 实操落地:配置、代码与观测手段
4.1 哪些开关能直接影响分配均衡度
先说最直接的配置项,在conf/flink-conf.yaml里:
yaml复制cluster.evenly-spread-out-slots: true
这个参数把调度策略从默认的SLOT_CONSTELLATION切换为SLOT_SPREAD。开启后,同一个作业的Slot会尽量均匀分布到所有TaskManager上。对于并行度不一致的多个作业混跑场景,它比默认策略更容易让整集群的Slot占用率趋向均匀。它需要在提交作业前设置,并且对之后提交的所有作业生效。
但我要强调一下:这个参数不是万能的。它在单个作业维度做均匀化,无法精确控制多个作业叠加后的全局最优。比如作业A并行度10,3台TM各4 Slot,SLOT_SPREAD会铺成4、4、2;作业B并行度6再上来,它会把B的Slot铺到3台机器上,最终大概率是5、5、6之类,已经比默认策略好很多,但仍有细粒度优化空间。
如果你用的是Flink 1.16及以上版本,调度器内部对Slot分配逻辑做了一些调整,引入了更灵活的SlotAllocationStrategy接口,社区也在推动分配策略更关注全局均衡。实际使用中,我建议不要只依赖某个单一开关,而是结合下面的代码层面规划一起做。
另外还有一个容易忽略的参数:
yaml复制slotmanager.max-total-resource: 4gb
slotmanager.taskmanager-timeout: 30000
slotmanager.max-total-resource控制的是集群能申请到的TaskManager总资源上限,它间接影响Slot总量。在一些按需扩容的环境里,如果你把这个上限设得太低,导致TaskManager数量不足,那么无论分配策略多均衡,Slot总量都不够,作业只能排队等待。之前排查过一个问题,作业并行度只有8,集群显示有一堆空闲CPU,但作业就是起不来,最后发现是max-total-resource被配成了1gb,TaskManager只能开一个,Slot总数远小于作业需求。
4.2 作业代码层面的主动规划
除了改全局配置,我更推荐在作业提交前主动规划Slot Sharing Group。默认情况下所有算子共用一个group,这会造成一个Slot里串起整条pipeline,而不同并行度的算子被强制对齐到同一个并行度上限。比如一个作业Source并行度是2,Sink并行度是10,默认共享组会让整个作业的并行度变成max(2,10)=10,其中Source的8个并行实例实际上是空转的,白白占着Slot。
实际操作中,把并行度差异大的算子分成不同的SlotSharingGroup,可以有效减少Slot浪费,也就间接提升了TaskManager的利用均匀度。在DataStream API里这样写:
java复制source.name("source")
.slotSharingGroup("light-group")
.map(new HeavyMapFunction())
.slotSharingGroup("heavy-group")
.sinkTo(new KafkaSink<>())
.slotSharingGroup("sink-group");
每个group对应独立的Slot占用。如果heavy-group的并行度是8,light-group并行度是2,那么heavy-group会单独占8个Slot,light-group占2个Slot,不会互相拖累。这样集群的Slot分配会更贴近真实负载,而不是被某个低并行度算子的空转实例填满。你在Web UI上看到的TaskManager分布也会更清晰:哪个TM在跑重计算,哪个TM在跑轻量的Source/Sink,一目了然。
另一个代码层面的技巧是显式调配并行度,尽量让多个作业并行度的组合能整除集群Slot总数。比如3台TM每台4 Slot,两个作业并行度分别是4和8,那正好占满且均匀;如果一个是5一个是7,就会出现一台机器空一个Slot的碎片。我一般会做一次简单的LCM(最小公倍数)估算:先算集群总Slot数,再算当前所有作业的并行度总和,尽量让它们匹配。当然,业务上的实时延迟和吞吐约束优先,这个只是辅助优化手段。
4.3 怎么判断当前集群到底均不均衡
要想评估调度策略调得怎么样,不能只靠肉眼扫Web UI。我用三个指标来衡量:
- Slot占用率方差:统计每台TaskManager的
已分配Slot/总Slot,算方差或变异系数。变异系数小于0.15基本就算健康,大于0.3说明明显不均衡。 - 每台TM的活跃Task数量:活跃Task数量不等于Slot分配数,因为Slot共享组不同时,一个Slot可能只跑一个算子。活跃Task数能更真实反映CPU和内存压力。
- 每台TM的背压状态与GC耗时:如果某台TM的背压比例明显高于其他TM,即使Slot分布看起来均衡,也可能是因为数据倾斜,或者是Slot上跑的算子太重。这时候需要从上游keyBy策略或算子并行度下手,而不是继续调调度器。
Web UI上的TaskManager页面,会显示每台机器的Slot数、任务数、CPU、内存、网络指标。我建议把它导出来或者定时截图,和作业提交记录做对照。这样你能看到“每次提交作业后,集群分布是怎么变化的”。另一个高频使用的工具是Flink REST API:
bash复制curl http://localhost:8081/taskmanagers
返回的JSON里每台TM都有slotsNumber和freeSlotsNumber字段,写个小脚本就能算出各TM的占用率,再求个标准差。我实际维护的集群上,就是用这个脚本做定期巡检,能提前发现分配不均衡的趋势,而不是等CPU告警了才去看。
4.4 实测效果对比
我专门做了一个对照实验:3台TaskManager,每台4 Slot,两个作业并行度分别为10和2,共12个Slot需求。
默认策略(SLOT_CONSTELLATION)下,分布是TM1=8、TM2=2、TM3=2,变异系数约0.77。开启cluster.evenly-spread-out-slots: true之后,分布变成TM1=4、TM2=4、TM3=4,变异系数降到0,完美均匀。这组测试说明,对于并行度组合能整除的场景,切换SLOT_SPREAD策略就能直接解决问题。
但如果是3台TM每台4 Slot,两个作业并行度分别为7和5,总需求12。SLOT_SPREAD会先把作业A(并行度7)铺成3、2、2,再把作业B(并行度5)铺到占用率最低的TM,最终大概是TM1=4、TM2=4、TM3=4。这是理想情况,因为7和5的组合恰好能填满。如果是7和6,总需求13,但总Slot只有12,有一个作业需要等待或者被拒绝,这时候分配策略再均衡也没有用,需要调整并行度或者增加TM。
所以我的结论是:均衡分配策略解决的是“在Slot总量充足的前提下,怎么把负载压均匀”,而不是“增加Slot总量”。排查顺序永远是先看总Slot够不够,再看分配策略对不对,最后才看单个TM上的算子重不重。
5. 常见问题与排查技巧实录
5.1 高频问题速查表
| 现象 | 可能原因 | 排查手段 |
|---|---|---|
| 某台TM的Slot全满,其他TM大量空闲 | 多个作业并行度不一致,默认SLOT_CONSTELLATION聚堆 | 开启cluster.evenly-spread-out-slots,或手动规划SlotSharingGroup |
| 所有TM的Slot占用率差不多,但某台CPU明显偏高 | Slot内算子负载不同,大状态或Window算子扎堆 | 查看该TM上的Task列表,用slotSharingGroup隔离重算子 |
| 提交新作业后等待很长时间,但不报资源不足 | Slot总量够,但分配策略触发等待时机不对 | 检查slotmanager.max-total-resource和TaskManager上下线状态 |
| 开启spread策略后,作业吞吐下降 | 作业内部shuffle量大,跨TM传输变多 | 只对多作业混跑集群开启spread;对单个大作业保持默认策略 |
| 任务总是卡在SCHEDULED状态 | 某个TM的可用Slot碎片化,不足整组分配 | 调整并行度接近Slot总数,或重启TM清理碎片 |
| 用管理平台(比如DataSophon)部署后提交作业失败 | 平台环境下临时目录权限或类加载冲突 | 查看JobManager日志,重点看java.io.tmpdir和jar包冲突 |
5.2 我踩过的几个坑
第一个坑是盲目开启cluster.evenly-spread-out-slots。我有一个作业内部有大量的keyBy + Window聚合,shuffle量极大。开启spread策略后,Slot被铺开到6台机器上,数据从map阶段跨网络传到window阶段,网络开销翻了快一倍,端到端延迟从200ms涨到500ms。后来我把这个作业单独提到一个集群,用默认策略跑,延迟又回去了。所以均衡策略是给“多作业混跑、强调整体资源利用率”的场景用的,单作业大shuffle场景还是默认策略更合适。
第二个坑是忽略SlotSharingGroup的默认行为。有一次我为了提升均衡度,把作业里所有算子的并行度都调成一样的,结果并行度从6改成12,集群Slot直接翻倍占用,实际上很多实例都在空转。后来才意识到,默认共享组会把所有算子绑到一个并行度上,修改并行度实际改变的是整个pipeline的Slot需求,而不是某个算子的。正确做法是先拆分SlotSharingGroup,再分别调整并行度。
第三个坑跟JDBC连接器有关。我用Flink CDC同步数据时,Sink端用的JDBC连接器并行度设得很高,每个subtask都建立了独立的数据库连接,导致其中一台TM上的连接数暴涨,数据库端先把那台TM的IP给限流了。表面上看是TM不均衡,实际上是JDBC连接池配置问题。调整Sink并行度并开启连接复用后,负载自然就均衡了。这事提醒我:调度层面的均衡只是第一步,连接池、线程池这些资源也需要跟着并行度走。
第四个坑是DataSophon这类管理平台上的Flink部署问题。我用管理平台装好Flink后,提交Job一直失败,看日志发现是java.io.tmpdir指向了一个没有写权限的目录,任务在初始化阶段就挂掉。后来在平台里给Flink进程配置了独立的临时目录并放开权限才算解决。如果你也是通过平台管理Flink,遇到“不能上传job”或“提交Job失败”,先别怀疑调度器,先确认平台部署时路径、权限、环境变量这些基础项。
5.3 排查顺序建议
我自己现在排查“TM负载不均”类问题时,固定按这个顺序走:
- 先看总Slot数和作业并行度之和是否匹配。不匹配时先不调调度策略,优先调整并行度或增加TM。
- 再看Web UI的TaskManager列表,确认Slot占用排名前几的机器之间的差异。差异超过30%才值得继续查。
- 如果确认是多个作业混跑导致的,开启
cluster.evenly-spread-out-slots,同时观察那个shuffle量特别大的作业是否受影响。 - 如果开启spread后某个大作业性能下降,考虑用
slotSharingGroup把重算子单独隔离,而不是一刀切全开spread。 - 最后看单台TM的背压、GC和连接数,排除数据倾斜和外部系统瓶颈。
这套顺序能帮我快速区分:是资源总量问题、分配策略问题,还是单个算子负载问题。这三者的修法完全不同,搞混了只会浪费半天时间。
最后说两句个人体会
Balanced Tasks Scheduling在我看来不是一个标准化的Flink官方术语,而是一类“让TaskManager负载更均匀”的策略集合。它背后真正的价值,是把调度视角从单作业拉高到集群全局。我维护过几个实时集群,最深的体会是:Flink的调度器在单个作业维度上已经做得很好,但多个作业并行度不一致时,默认逻辑确实会偏向局部最优,导致全局出现资源碎片和热点。解决办法没有银弹,需要“全局配置 + 作业级SlotSharingGroup规划 + 持续观测”三管齐下。如果你也在跑多作业共享集群,建议下次提交前先打开Web UI看一眼TaskManager的占用分布,把占用率最高的那台机器点开,看看上面都堆了哪些作业的子任务——这个习惯能帮你提前发现很多潜在的性能瓶颈。
