注意:如果task的任务数据也就是并行度大于> slot,那么程序无法运行。
1、一个TaskManager里面默认只有一个slot
2、在task运行的过程中会进行数据合并,比如说下图的KeyBy --> Map 会产生operator Chain的情况
Operator Chain的条件:
1、数据的传输策略是: forward strategy
2、在同一个taskManager中运行
3、taskManager会尽量保证task在同一个jvm里面运行,有利于提升效率。
Flink TaskSlot与并行度
taskSlots的数量对并行度的影响?
TaskManager 是一个 JVM 进程,是实际负责执行计算的Worker,TaskManager中最小的资源调度单位是TaskSlots。TaskManger从 JobManager 接收需要执行的任务,然后申请Slot 资源(根据集群Slot使用情况以及并行度设置)并尝试启动Task开始执行作业,会以独立的线程来执行一个task或多个subtask。为了控制一个 TaskManager 能执行多少个 task,Flink 提出了 Task Slot 的概念…
Flink的Task、SubTask
Task可以理解为Flink作业计算时的算子 比如 map、keyBy等等
但是由于flink的taskmanager在运行task的时候是每个task采用一个单独的线程,这就会带来很多线程切换开销,进而影响吞吐量。为了减轻这种情况,flink进行了优化,也即对subtask进行链式操作,链式操作结束之后得到的task再作为一个调度执行单元,放到一个线程里执行。如下图的,source/map 两个算子进行了链式 合成了一个算子链(可理解为合并为一个算子);keyby/window/apply三个算子也进行了链式组合为了算子链(可理解为合并为一个算子),sink为单独的一个算子。
SubTask可以理解算子在运行时根据并行度设置而产生的运算算子实例
如下图所示:
说明:图中假设是source/map的并行度都是2,keyby/window/apply的并行度也都是2,sink的并行度是1,则此Flink作业task有五个,则会由五个不同的线程执行。
什么情况下算子可以组合为算子链?
-
上下游的并行度一致
-
下游节点的入并行度为1 (也就是说下游算子节点没有来自其他算子节点的输入)
-
上下游节点都在同一个 slot group 中
为了防止同一个slot包含太多的task,Flink提供了资源组(group)的概念。group就是对operator(算子)进行分组,同一group的不同operator task可以共享同一个slot。默认所有operator属于同一个组"default",也就是所有operator task可以共享一个slot。我们可以通过slotSharingGroup()为不同的operator设置不同的group。
dataStream.filter(e->e.getId()!=0).slotSharingGroup("groupName");
下游节点的 chain 策略为 ALWAYS(可以与上下游算子链接,map、flatmap、filter等默认是ALWAYS)
上游节点的 chain 策略为 ALWAYS 或 HEAD(只能与下游链接,不能与上游链接,Source默认是HEAD)
两个节点间数据分区方式是 forward
用户没有禁用 chain
并行度
一个Flink程序由多个任务(Task)组成(source、transformation和 sink)。 一个任务由多个并行的实例(线程)来执行(SubTask), 一个任务的并行实例(线程)数目就被称为该任务的并行度。
Flink中的程序本质上是并行的和分布式的。在执行期间,一个流具有一个或多个流分区,并且每个算子具有一个或多个算子子任务。算子子任务之间彼此独立,并可以在在不同的线程(甚至服务器)中执行,算子的并行度决定了算子子任务数量,同一程序的不同算子可设置不同的并行度。
TaskSlot与并行度的联系
既然Flink作业在执行的时候,是需要申请Slot资源(根据并行度),然后启动Task执行作业,那么TaskSlot与并行度到底什么关系呢?
我们以上图为例子 (source/map的并行度都是2,keyby/window/apply的并行度也都是2,sink的并行度是1)
假如,我们现在有两个TaskManger,且每个TaskManger都有三个TaskSlot,那么 如上示例subtask在Slot可能是这样分布的…
上边这幅图呢,表示了 source/map算子链 与 keyby/window/apply 算子链的两个subtask都进入了不同的Task Slot ,sink单独进入了一个TaskSlot,由于Taskmanger 会根据TaskSlot数量 对每个TaskSlot平分系统资源,但是呢,我们发现,上述情况,有一个TaskSlot为空闲状态并未使用,因为白白浪费了系统资源…
为什么会出现这种情况呢?因为并行度设置不合理导致的…由于上方 2+2+1 并行度,总共才会有2个subtask,就算每个subtask都进入了不同的TaskSlot,仍会有TaskSlot为空闲状态 (因为上图 TaskSlot有6个…)
这个时候呢,为了能充分利用slot资源,我们需要对我们的Flink作业并行度进行优化设置
比如,我们设置source/map、keyby/window/apply的算子链并行度为6(并行度为6,表示算子(Task)的并行数为6,即同时可有6个subTask执行),sink并行度保持为1
每一个 sourec/map keyBy/window/apply 算子链进入到不同TaskSlot 如下图所示
那么,此时,我们的TaskSlot都被利用到了,就能充分利用slot资源,同时保证每个TaskManager能平均分配到重的subtasks,比如keyby/window/apply操作就会均分到申请的所有slot里,也保证了slot的负载均衡
如果看到这里,您还不太清楚,我再上一张官网示例图:
说明:当前Flink集群一共有三个TaskManger,每个TaskManger有三个TaskSlot (那么Flink集群总可用TaskSlot为 3*3=9个)
1、我们设置作业并行度为1,将占用1个Slot
(默认的并行度为1)
那么,Flink程序的所有Task 与SubTask 均会在一个TaskSlot中, 就如同上图,现在在TaskManger1 中 第一个Slot,此时Flink程序使用着Flink集群中1/9的资源执行作业,余下的8/9皆为空闲状态
2、我们设置作业并行度为2后,将占用2个Slot
此时,我们的Flink程序的所有Task 与SubTask 会在两个TaskSlot中, 就如同上图,现在在TaskManger1 中 第一个Slot,与TaskManger2中第一个Slot,此时Flink程序使用着Flink集群中2/9的资源执行作业,余下的7/9皆为空闲状态
3、我们设置作业并行度为9后,将占用9个Slot
我们所有的Source、Reduce、SInk 算子子任务 分配到了每个Slot,此时Flink程序使用着Flink集群中9/9的资源执行作业,TaskSlot利用率为100%
4、我们设置作业算子Source、Reduce并行度为9 Sink并行度为1
此时,我们所有的Source、Reduce算子子任务分配到了每个Slot,但是由于Sink并行度为1,则只会进入其中一个Slot中,此时Flink程序使用着Flink集群中9/9的资源执行作业,TaskSlot利用率为100%,与示例(3)不同的是 我们输出并行度为1,无论哪一组Source、Reduce subTask执行完毕后,都只会交由一个且仅有一个sink的subTask执行数据输出。
并行度设置注意事项
注意点:并行度改变会影响任务划分,与subtask数量,如果taskslots数量不满足要求,会导致任务没有足够的资源分配,但flink会尝试申请资源(5分钟)然后才会关闭计算程序
并行度设置
-
算子
DataStream<String> result = windowedStream.apply(new SpeedAlarmWindow()).setParallelism(1);
环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(12);
-
客户端
-
系统
flink-config.yaml文件中设置
并行度优先级
算子级别>环境>客户端>全局系统
并行度Parallelism与任务槽TaskSlot总结
TaskSlot是静态的概念,代表着Taskmanager具有的并发执行能力
parallelism是动态的概念,是指程序运行时实际使用的并发能力
设置合适的parallelism能提高运算效率 不可太多也不可太少(多了导致无法申请可用资源 程序无法正常执行,少了资源浪费,并发执行力度浪费)
Local模式下注意事项
计算程序开发过程中,默认并行度为Cpu核心数数量