Flink-Streaming之StreamPartitioner

在介绍流分区器前,为方便大家理解,我们解释下文中会用到的Flink几个关键名词概念。
Parallelism:Flink中Parallelism表示并行度,假设source算子的parallelism=2,集群有2个TaskManager,每个TaskMananger有一个slot,那么source算子会同时在2个TaskManager中运行。
Operator:一个算子,比如map算子就是Operator
UpStream:上游流
DownStream:下游流
文中的例子upStream为source算子,downStream为map算子。现在回到分区器内容中来,假设parallelism=2,当source[0]中数据T需要emit给map时,给map[0]或者map[1]哪个最优? 

Flink-Streaming之StreamPartitioner_第1张图片

最优分区器评估标准:
  1. 本地化
  2. 数据均衡
本地化和数据均衡跟鱼和熊掌不能兼得一个道理,在数据倾斜不是很夸张的提前下优先选择本地化策略。 

所有分区器都实现了StreamPartitioner接口selectChannel方法,返回选择的Channel。(Channel指map[0]或者map[1]实际运行所在的进程)

在selectChannel方法

int selectChannel(T record);

方法名

入参

出参

selectChannel

需要emit到downStream的数据

数据被分配到Channel

本地化策略

ForwardPartitioner是完全本地化分区器,没有跨节点数据传输,最优选择的分区器。RescalePartitioner是局部本地化分区器,其它都是全局分区器。
假设source--->map两个算子各自的并行度决定可使用的分区器。使用本地化分区器的算子都可组成OperatorChain

ForwardPartitioner

本地化分区器,数据传输不会出现跨节点,upStream、downStream会组成一个OperatorChain,这是最优的Partitioner策略。

 Flink-Streaming之StreamPartitioner_第2张图片

@Override
public int selectChannel(SerializationDelegateT>> record) {
  
return 0;
}

出度为1,只有一个Channel

RescalePartitioner

         采用轮询路由规则,局部分区策略,个人理解是介于本地化分区策略和全局化分区策略之间。

路由规则:

@Override
public int selectChannel(SerializationDelegateT>> record) {
  
if (++nextChannelToSendTo >= numberOfChannels) {
     
nextChannelToSendTo = 0;
   }
  
return nextChannelToSendTo;
}

自增变量nextChannelToSendTo决定数据选择channel的路由规则,当大于等于最大Channels数值时重置为0,在尽可能减少跨节点传输数据的情况下确保数据负载均衡。

GlobalPartitioner

全局分区器,数据只会路由到Partitioner为0的DownStream节点。

Flink-Streaming之StreamPartitioner_第3张图片

@Override
public int selectChannel(SerializationDelegate> record) {
   return 0;
}

当所有数据要做串行处理的时,这是一个不错的选择。

BroadcastPartitioner

         广播分区器,会把同一份数据路由给downStream所有的节点,这相当于对数据进行了复制,复制份数由setParallelism决定。

Flink-Streaming之StreamPartitioner_第4张图片

适用场景:数据字典,配置项类的数据通过广播分区策略传播到downStream每个节点

KeyGroupStreamPartitioner

键-组流分区器,基于消息键的hash值做简单的计算决定Channel,属于全局分区器。

数据路由物理图:

Flink-Streaming之StreamPartitioner_第5张图片

@Override
public int selectChannel(SerializationDelegate> record) {
   K key;
   try {
      key = keySelector.getKey(record.getInstance().getValue());
   } catch (Exception e) {
      throw new RuntimeException("Could not extract key from " + record.getInstance().getValue(), e);
   }
   return KeyGroupRangeAssignment.assignKeyToParallelOperator(key, maxParallelism, numberOfChannels);
}

算法:Murmurhash(key)%maxParallelism*parallelism/maxParallelism

缺点:当某个键的消息比较多时,会出现数据倾斜,解决数据倾斜的方法一般是对键加前缀打散处理,对数据聚合后再去掉前缀还原。

现在看重头戏,如何确定出度的数量。

RebalancePartitioner

       再平衡分区器,启动时随机选择一个Channel,然后使用取模算法选择Channel,达到了轮询效果

Flink-Streaming之StreamPartitioner_第6张图片

@Override
public int selectChannel(SerializationDelegate> record) {
   nextChannelToSendTo = (nextChannelToSendTo + 1) % numberOfChannels;
   return nextChannelToSendTo;
}

数据均衡上表现最好的一个分区器,在均衡上比ShufflePartitioner更出色,最大的缺点是数据跨节点传输量过大

ShufflePartitioner

混洗分区器,随机的方式把数据打散,数据均衡上也是表现良好的,比RebalancePartitioner略差,共同的缺点就是数据跨节点传输量过大

Flink-Streaming之StreamPartitioner_第7张图片

@Override
public int selectChannel(SerializationDelegate> record) {
   return random.nextInt(numberOfChannels);
}

CustomPartitionerWrapper

自定义分区器包装类,用户在构建此类实例前需要实现Partitioner接口,在方法partition中定义分区器路由规则。
使用自定义分区规则:
只需在DataStream类实例方法partitionCustom传入Partitioner实现类实例即可
@Override

dataStream.partitionCustom(new Partitioner(){

     public int partition(String key, int numPartitions){

         return hash(key)%numPartitions

}

})

出度:

env.setParallelism(4)

env.addSource(…).map(…).keyBy(…)

Flink-Streaming之StreamPartitioner_第8张图片

上图中并行度为4,那么Channel也为4。 

你可能感兴趣的:(Flink,flink)