flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码

文章目录

    • 前言
    • 1. 随机分区
    • 2. 轮询分区
    • 3. 重缩放分区
    • 4. 广播
    • 5. 全局分区
    • 6. 自定义分区

前言

  flink中keyBy是一种按照键的哈希值来进行重新分区的操作,至于分区是否均匀、每个key 的数据具体会分到哪一区无法控制,因此keyBy 是一种逻辑分区(logical partitioning)操作。只有物理分区(physical partitioning),才真正控制分区策略精准地调配数据。
  物理分区与 keyBy 另一大区别在于,keyBy 之后得到的是一个 KeyedStream,而物理分区之后结果仍是 DataStream,且流中元素数据类型保持不变。分区算子并不对数据进行转换处理,只是定义了数据的传输方式
  flink控制分区策略的顶层接口为ChannelSelector,其实现类实现selectChannel()方法决定数据的流向
flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第1张图片
如:keyby算子采用的分区器KeyGroupStreamPartitioner,其核心逻辑如下
flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第2张图片
flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第3张图片
flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第4张图片
对传入的record的key进行取hashCode


1. 随机分区

Partitions elements randomly according to a uniform distribution

  通过调用 dataStream 的shuffle()方法,将数据随机地分配到下游算子的并行任务中,因为是完全随机的,所以对于同样的输入数据, 每次执行得到的结果也不会相同。

flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第5张图片

dataStream.shuffle();

  底层采用分区器为:ShufflePartitioner,核心处理逻辑为:
在这里插入图片描述


2. 轮询分区

  按照先后顺序将数据做依次分发。通过调用dataStream 的rebalance()方法,就可以实现轮询重分区。rebalance使用的是 Round-Robin 负载均衡算法,可以将输入流数据平均分配到下游的并行任务中
flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第6张图片

dataStream.rebalance()

  底层采用分区器为:RebalancePartitioner,核心处理逻辑为:
在这里插入图片描述


3. 重缩放分区

  重缩放分区和轮询分区非常相似。当调用 rescale()方法时,其实底层也是使用 Round-Robin算法进行轮询,但是只会将数据轮询发送到下游并行任务的一部分中。 rebalance 的方式是每个发牌人都面向所有人发牌;而 rescale的做法是分成小团体,发牌人只给自己团体内的所有人轮流发牌。
flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第7张图片

  由于 rebalance 是所有分区数据的“重新平衡”,当 TaskManager 数据量较多时,这种跨节点的网络传输必然影响效率;而如果配置的 task slot 数量合适,用 rescale 的方式进行“局部重缩放”,就可以让数据只在当前 TaskManager 的多个 slot 之间重新分配,从而避免了网络传输带来的损耗。
  从底层实现上看,rebalance 和 rescale 的根本区别在于任务之间的连接机制不同。rebalance将会针对所有上游任务(发送数据方)和所有下游任务(接收数据方)之间建立通信通道,这是一个笛卡尔积的关系;而 rescale 仅仅针对每一个任务和下游对应的部分任务之间建立通信通道,节省了很多资源。

dataStream.rescale()

  底层采用分区器为:RescalePartitioner,核心处理逻辑为:
flink keyby、shuffle、 rebalance、rescale、 broadcast、global、自定义分区算子以及各分区器源码_第8张图片


4. 广播

  经过广播之后,数据会在不同的分区都保留一份,可能进行重复处理。可以通过调用 dataStream 的 broadcast()方法,将输入数据复制并发送到下游算子的所有并行任务中去。

dataStream.broadcast();

5. 全局分区

  通过调用dataStream的global()方法,会将所有的输入流数据都发送到下游算子的第一个并行子任务中去。这就相当于强行让下游任务并行度变成了 1,所以使用这个操作需要非常谨慎,可能对程序造成很大的压力

dataStream.global();

6. 自定义分区

  当 Flink 提 供 的 所 有 分 区 策 略 都 不 能 满 足 用 户 的 需 求 时 , 可 以 通 过 使 用partitionCustom()方法来自定义分区策略。

dataStream.partitionCustom();

  在调用时,方法需要传入两个参数,第一个是自定义分区器(Partitioner)对象,第二个是应用分区器的字段,:通过字段名称或字段位置索引指定,也可以实现一个 KeySelector

public class CustomPartitionTest {
    //对一组自然数按照奇偶性进行重分区
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env =
                StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 将自然数按照奇偶分区
        env.fromElements(1, 2, 3, 4, 5, 6, 7, 8)
                .partitionCustom(new Partitioner<Integer>() {
                    @Override
                    public int partition(Integer key, int numPartitions) {
                        return key % 2;
                    }
                }, new KeySelector<Integer, Integer>() {
                    @Override
                    public Integer getKey(Integer value) throws Exception {
                        return value;
                    }
                })
                .print()
                .setParallelism(2);
        env.execute();
    }
}

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