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

本文详细介绍了Apache Flink中的数据分区策略,包括随机分区、轮询分区、重缩放分区、广播、全局分区以及自定义分区。这些策略影响数据在并行任务间的分布,从而影响处理效率和结果。例如,随机分区实现数据均匀分布,轮询分区确保负载均衡,重缩放分区则在局部进行数据调整,广播分区将数据复制到所有任务,全局分区将所有数据发送至同一任务,自定义分区允许用户根据需求定制策略。

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

前言

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


1. 随机分区

Partitions elements randomly according to a uniform distribution

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

在这里插入图片描述

dataStream.shuffle();

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


2. 轮询分区

  按照先后顺序将数据做依次分发。通过调用dataStream 的rebalance()方法,就可以实现轮询重分区。rebalance使用的是 Round-Robin 负载均衡算法,可以将输入流数据平均分配到下游的并行任务中
在这里插入图片描述

dataStream.rebalance()

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


3. 重缩放分区

  重缩放分区和轮询分区非常相似。当调用 rescale()方法时,其实底层也是使用 Round-Robin算法进行轮询,但是只会将数据轮询发送到下游并行任务的一部分中。 rebalance 的方式是每个发牌人都面向所有人发牌;而 rescale的做法是分成小团体,发牌人只给自己团体内的所有人轮流发牌。
在这里插入图片描述

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

dataStream.rescale()

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


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();
    }
}
<think>嗯,用户问的是Flink中涉及重分区算子。首先,我得回忆一下Flink算子有哪些,特别是涉及到数据重分布的那些。重分区通常是指改变数据在并行任务之间的分布方式,这会影响任务的并行度和数据处理的效率。 首先,常见的重分区算子可能有keyBy,因为keyBy会根据指定的键将数据重新分组到不同的分区,这样相同键的数据会被发送到同一个任务实例。然后是shuffle,这个算子会随机地将数据均匀分布到下游任务,不过用户可能需要注意shuffle的具体行为。 接下来还有rebalance,这个算子用轮询的方式均匀分配数据,用来处理数据倾斜的问题。不过有时候用户可能会混淆rebalancerescale的区别,rescale是在本地进行数据交换,可能不会跨网络,而rebalance是全局的,所以需要区分清楚。 另外,还有global,这个算子会把所有数据发送到下游的第一个任务,所以会导致全局聚合,但并行度会被强制为1。而forward则要求上下游并行度一致,数据不会重新分区,只是直接传递,所以可能不算严格的重分区算子,但用户可能想知道它的特殊情况。 还有partitionCustom,允许用户自定义分区策略,这比较灵活,但需要用户自己实现Partitioner接口。可能需要举一个例子说明。 需要确保每个算子的解释准确,同时说明它们的适用场景和可能带来的影响,比如网络开销、数据倾斜的解决等。同时,要提醒用户在使用这些算子时要注意的性能问题,比如shuffle可能带来较大的网络负载,而keyBy在窗口聚合时很常见。 另外,用户可能想知道这些算子如何影响作业的并行结构,比如改变并行度或者数据分布方式。所以需要明确每个算子对应的数据分区变化,以及上下游并行度的关系。 可能还需要提到,重分区操作通常会触发网络传输,属于比较昂贵的操作,所以在实际应用中要合理使用,避免不必要的重分区导致性能下降。例如,在数据已经按某个键分布好的情况下,再次使用keyBy可能没有必要,反而增加开销。 最后,总结一下各个算子的区别和适用场景,帮助用户根据具体需求选择合适的重分区策略。确保回答结构清晰,分点列出每个算子,并给出示例,这样用户更容易理解。</think>在 Apache Flink 中,涉及重分区算子会改变数据在并行任务间的分布方式,直接影响任务的并行结构和性能。以下是常见的重分区算子及其原理和用途: --- ### 1. **`keyBy`** - **作用**:根据指定的键(Key)对数据进行哈希分区,相同键的数据会被分配到同一分区。 - **触发条件**:需要后续聚合操作(如 `reduce`、`window`)时使用。 - **示例**: ```java DataStream<Tuple2<String, Integer>> stream = ...; KeyedStream<Tuple2<String, Integer>, String> keyedStream = stream.keyBy(value -> value.f0); ``` - **特点**: - 分区数由下游算子并行度决定。 - 可能导致数据倾斜(某些分区负载过高)。 --- ### 2. **`shuffle`** - **作用**:完全随机地将数据均匀分布到下游所有任务。 - **适用场景**:需要完全打乱数据分布时使用。 - **示例**: ```java DataStream<String> shuffled = stream.shuffle(); ``` - **特点**: - 数据分布均匀,但网络开销较大。 - 不保证任何顺序。 --- ### 3. **`rebalance`** - **作用**:以轮询(Round-Robin)方式均匀分配数据到下游任务。 - **适用场景**:解决数据倾斜问题。 - **示例**: ```java DataStream<String> rebalanced = stream.rebalance(); ``` - **特点**: - 强制全局数据平衡。 - 适合上游并行度与下游不一致的场景。 --- ### 4. **`rescale`** - **作用**:在本地 TaskManager 内部进行轮询分区,避免跨节点网络传输。 - **示例**: ```java DataStream<String> rescaled = stream.rescale(); ``` - **特点**: - 仅在本地交换数据,网络开销低。 - 要求上下游并行度是倍数关系(如上游并行度 4,下游并行度 2)。 --- ### 5. **`global`** - **作用**:将所有数据发送到下游的第一个并行任务。 - **适用场景**:全局聚合操作(如 `count()`)。 - **示例**: ```java DataStream<String> global = stream.global(); ``` - **特点**: - 下游并行度被强制为 1。 - 可能导致性能瓶颈。 --- ### 6. **`forward`** - **作用**:数据直接传递到下游同一并行任务(上下游并行度必须相同)。 - **示例**: ```java DataStream<String> forwarded = stream.forward(); ``` - **特点**: - 无网络开销,但要求严格并行度匹配。 - 适用于链式优化(Operator Chaining)。 --- ### 7. **`partitionCustom`** - **作用**:自定义分区策略。 - **示例**(按奇偶分区): ```java stream.partitionCustom( (key, numPartitions) -> key % 2, value -> value // 提取分区键 ); ``` - **特点**: - 灵活性高,需实现 `Partitioner` 接口。 - 可能引入复杂逻辑。 --- ### **重分区的影响** 1. **网络开销**:跨节点重分区(如 `shuffle`、`keyBy`)会触发网络传输。 2. **数据倾斜**:不均匀的分区策略(如 `keyBy` 键分布不均)可能导致某些任务过载。 3. **并行度变化**:重分区后下游算子的并行度决定实际分区数。 --- ### **最佳实践** - 优先使用 `keyBy` 配合聚合操作。 - 数据倾斜时尝试 `rebalance` 或自定义分区。 - 避免不必要的重分区(如连续多个 `shuffle`)。 - 通过 Flink Web UI 监控反压和数据分布。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

但行益事莫问前程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值