Flink分区之Flink分区策略整理


title: Flink系列


一、Flink Partitioner 分区策略整理

Flink 的分区策略:

	批处理的分区策略: Partitioner
	流处理的分区策略: StreamPartitioner

关于 Flink 的分区策略,请看代码

1.1 批处理之Rebalance分区

package com.aa.flinkjava.partitioner;

import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.PartitionOperator;

/**
 * @Author AA
 * @Project bigdatapre
 * @Package com.aa.flinkjava.partitioner
 * Flink分区批处理的分区策略有 :
 * 1、rebalance
 * 2、partitionByHash
 * 3、partitionByRange
 * 4、partitionByCustom
 */
public class FlinkBatchPartitionerRebalance {
    public static void main(String[] args) throws Exception {
        //1、获取批处理编程入口
        ExecutionEnvironment executionEnvironment = ExecutionEnvironment.getExecutionEnvironment();

        //2、准备数据
        DataSource<Integer> dataSource = executionEnvironment.fromElements(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 14, 13, 12, 11);

        //3、测试一下rebalance分区策略
        //rebalance 其实就是 采用 Round-Robin 轮询的方式
        PartitionOperator<Integer> result = dataSource.rebalance().setParallelism(3);

        //4、输出数据
        /**
         * 因为设置了并行度是3,所以结果是3个文件,分别表示的是是三个分区的结果数据:
         * 分区1: 1、4、7、10、13
         * 分区2: 2、5、8、15、12
         * 分区3: 3、6、9、14、11
         *
         * 给后面的几个数据故意的调换了位置,就是为了测试这个效果。
         */
        //如果结果是一个文件,那么FlinkBatchPartitionerRebalance就是一个文件。
        //如果结果是多个文件,那么FlinkBatchPartitionerRebalance是文件夹。
        result.writeAsText("D:\\flinkres\\FlinkBatchPartitionerRebalance");

        //5、执行数据
        executionEnvironment.execute("FlinkBatchPartitionerRebalance");
    }
}

1.2 批处理之Range分区

package com.aa.flinkjava.partitioner;

import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.PartitionOperator;

/**
 * @Author AA
 * @Project bigdatapre
 * @Package com.aa.flinkjava.partitioner
 * Flink分区批处理的分区策略有 :
 * 1、rebalance
 * 2、partitionByHash
 * 3、partitionByRange
 * 4、partitionByCustom
 */
public class FlinkBatchPartitionerPartitionByRange {
    public static void main(String[] args) throws Exception {
        //1、获取批处理编程入口
        ExecutionEnvironment executionEnvironment = ExecutionEnvironment.getExecutionEnvironment();

        //2、准备数据
        DataSource<Integer> dataSource = executionEnvironment.fromElements(
                1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6);

        //3、测试一下分区策略
        //partitionByRange   是一个范围上的区分。
        PartitionOperator<Integer> result = dataSource.partitionByRange(integer -> integer).setParallelism(3);

        //4、输出数据
        /**
         * 因为设置了并行度是3,所以结果是3个文件,分别表示的是是三个分区的结果数据:
         * 分区1: 1-3的范围
         * 分区2: 4-6的范围
         * 分区3: 7-9的范围
         *
         */
        result.writeAsText("D:\\flinkres\\FlinkBatchPartitionerPartitionByRange");

        //5、执行数据
        executionEnvironment.execute("FlinkBatchPartitionerPartitionByRange");
    }
}

1.3 批处理之Hash分区

package com.aa.flinkjava.partitioner;

import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.PartitionOperator;

/**
 * @Author AA
 * @Project bigdatapre
 * @Package com.aa.flinkjava.partitioner
 */
public class FlinkBatchPartitionerPartitionByHash {
    public static void main(String[] args) throws Exception {
        //1、获取批处理编程入口
        ExecutionEnvironment executionEnvironment = ExecutionEnvironment.getExecutionEnvironment();

        //2、准备数据
        DataSource<Integer> dataSource = executionEnvironment.fromElements(
                1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6);

        //3、测试一下分区策略
        //partitionByHash  哈希分区,先求哈希,然后模除以分区数(并行度)
        PartitionOperator<Integer> result = dataSource.partitionByHash(integer -> integer).setParallelism(3);

        //4、输出数据
        /**
         * 因为设置了并行度是3,所以结果是3个文件,分别表示的是是三个分区的结果数据:
         * 分区1: 1,5,6,1,5,6
         * 分区2: 4,4
         * 分区3: 2,3,7,8,9,2,3
         *
         * 哈希分区: 相同的数据一定在一个分区中。
         */
        result.writeAsText("D:\\flinkres\\FlinkBatchPartitionerPartitionByHash");

        //5、执行数据
        executionEnvironment.execute("FlinkBatchPartitionerPartitionByHash");
    }
}

1.4 批处理之自定义分区

package com.aa.flinkjava.partitioner;

import org.apache.flink.api.common.functions.Partitioner;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.PartitionOperator;

/**
 * @Author AA
 * @Project bigdatapre
 * @Package com.aa.flinkjava.partitioner
 *
 * 自定义分区案例
 */
public class FlinkBatchPartitionerCustom {
    public static void main(String[] args) throws Exception {
        //1、获取批处理编程入口
        ExecutionEnvironment executionEnvironment = ExecutionEnvironment.getExecutionEnvironment();

        //2、准备数据
        DataSource<Integer> dataSource = executionEnvironment.fromElements(
                1, 2, 3, 4, 5, 60, 70, 80, 90, 10, 200, 300, 400, 500, 600);

        //3、测试一下分区策略
        //partitionCustom  自定义分区。
        PartitionOperator<Integer> result = dataSource.partitionCustom(new MyPartitioner(),word->word).setParallelism(3);

        //4、输出数据
        result.writeAsText("D:\\flinkres\\FlinkBatchPartitionerCustom");

        //5、执行数据
        executionEnvironment.execute("FlinkBatchPartitionerCustom");
    }

    static class MyPartitioner implements Partitioner<Integer>{

        /**
         * 定义具体的分区规则
         * 比如:
         * 将数据的数据的按照如下的规则进行划分:
         * 范围  <= 9   在一个分区
         * 范围  10-99  在一个分区
         * 范围  > 99   在一个分区
         * @param integer  输入的数据
         * @param numPartitions  分区的个数  现在在如下的案例中分区的数是3个,也就是输入的并行度。
         * @return
         */
        @Override
        public int partition(Integer integer, int numPartitions) {
            if (integer <= 9){
                return 0;
            }else if (integer >= 10 && integer <= 99){
                return 1;
            }else {
                return 2;
            }
        }
    }
}

1.5 流处理之内置N 大分区

package com.aa.flinkjava.partitioner;

import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * @Author AA
 * @Project bigdatapre
 * @Package com.aa.flinkjava.partitioner
 * Flink流中 内置的分区策略
 */
public class FlinkStreamPartitionerBuiltin {
    public static void main(String[] args) {
        //1、获取环境变量
        StreamExecutionEnvironment executionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment();

        //2、读取数据源
        DataStreamSource<String> dataStreamSource = executionEnvironment.socketTextStream("hadoop12", 9999);

        //3、下面都是分区的测试了。
        //分区一、伸缩 。 1-》N 或者 N-》1
        DataStream<String> rescaleDS = dataStreamSource.rescale();

        //分区二、rebalance 其实就是轮询
        DataStream<String> rebalanceDS = dataStreamSource.rebalance();

        //分区三、keyBy。 根据指定的字段来进行分区,相同的key必然在一个分区中。按照指定的key来做hash
        KeyedStream<String, Tuple> keyByDS = dataStreamSource.keyBy(1);

        //分区四、global 。 所有的输出数据都发送到下游的第一个task
        DataStream<String> globalDS = dataStreamSource.global();

        //分区五、forward。 上下游的本地task映射
        DataStream<String> forwardDS = dataStreamSource.forward();

        //分区六、shuffle。 随机分区。 随机均匀分布元素
        DataStream<String> shuffleDS = dataStreamSource.shuffle();

        //分区七、broadcast。广播。 向每个分区广播元素,帮所有的元素广播到所有的分区。
        DataStream<String> broadcastDS = dataStreamSource.broadcast();
    }
}

1.6 流处理之自定义分区

package com.aa.flinkjava.partitioner;

import org.apache.flink.api.common.functions.Partitioner;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * @Author AA
 * @Project bigdatapre
 * @Package com.aa.flinkjava.partitioner
 */
public class FlinkStreamPartitionerCustom {
    public static void main(String[] args) throws Exception {
        //1、获取环境变量
        StreamExecutionEnvironment executionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment();

        //2、读取数据源
        DataStreamSource<String> dataStreamSource = executionEnvironment.socketTextStream("hadoop12", 9999);

        //3、下面都是分区的测试了。
        DataStream<String> partitionCustomDS = dataStreamSource.partitionCustom(new MyStreamPartitioner(),word->word);

        //4、打印输出
        partitionCustomDS.print().setParallelism(1);

        //5、执行。
        executionEnvironment.execute();
    }

    static class MyStreamPartitioner implements Partitioner<String>{

        /**
         * @param key
         * @param numPartitions
         * @return
         */
        @Override
        public int partition(String key, int numPartitions) {
            return (key.hashCode() & Integer.MAX_VALUE) % numPartitions;
        }
    }
}



声明:
        文章中代码及相关语句为自己根据相应理解编写,文章中出现的相关图片为自己实践中的截图和相关技术对应的图片,若有相关异议,请联系删除。感谢。转载请注明出处,感谢。


By luoyepiaoxue2014

B站: https://space.bilibili.com/1523287361 点击打开链接
微博地址: http://weibo.com/luoyepiaoxue2014 点击打开链接

<think>我们之前讨论了Flink中负责执行子任务的组件,现在用户的问题转向了负载均衡的具体实现函数。根据用户提供的引用内容,特别是引用[3]和[4],我们可以知道Flink中负载均衡是通过分区器(Partitioner)实现的,而其中一种常用的分区策略是HashPartitioner(哈希分区器)。用户问题:**Flink实现负载均衡使用哪个函数?**关键点:-负载均衡在Flink中是通过数据分区策略来实现的,目的是将数据均匀分布到下游算子实例(子任务)上。-根据引用[4],HashPartitioner是一种基于哈希的分区策略,它使用哈希函数将数据流中的元素映射到多个分区。-具体步骤:对每个元素的键(或直接对元素)进行哈希运算,然后根据哈希值取模确定分区号。因此,Flink中实现负载均衡的核心函数是**哈希函数**,具体在`HashPartitioner`中使用。在Flink的API中,当我们使用`keyBy()`方法时,默认就会使用哈希分区器(HashPartitioner)来对数据进行分区,从而实现负载均衡。此外,引用[5]提到,Flink中Key的分配和子任务的分配是通过哈希函数实现的,并且Flink会自动管理子任务的分配,尽可能将KeyGroup均匀分配给不同的子任务。所以,回答用户的问题:Flink实现负载均衡主要使用**哈希分区器(HashPartitioner)**,其核心是**哈希函数**。在编程中,我们通常通过`keyBy()`方法来触发使用哈希分区器。下面我将详细说明:</think>### Flink中实现负载均衡的核心函数与机制 在Flink中,负载均衡主要通过**数据分区策略**实现,核心函数是`keyBy()`和`rebalance()`,底层依赖哈希分区器(`HashPartitioner`)和轮询分区器(`RebalancePartitioner`)。 #### 1. **哈希负载均衡:`keyBy()`函数** ```java DataStream<Tuple2<String, Integer>> stream = ...; DataStream<Tuple2<String, Integer>> balancedStream = stream .keyBy(value -> value.f0); // 基于Key的哈希值分区 ``` - **机制**: 1. 对每个元素的Key计算哈希值:`hashCode = hashFunction(key)` 2. 通过模运算确定目标分区:`partitionIndex = hashCode % numberOfSubtasks` - **特点**: - 相同Key的数据始终分配到同一子任务 - 依赖Key分布的均匀性实现负载均衡[^4][^5] - **适用场景**:Key分布均匀的聚合操作(如`sum()`、`reduce()`) #### 2. **强制轮询负载均衡:`rebalance()`函数** ```java DataStream<String> stream = ...; DataStream<String> balancedStream = stream .rebalance(); // 强制轮询分发 ``` - **机制**: 1. 采用**轮询策略**分发数据 2. 每个元素按顺序分配到下一个子任务 3. 分区计算:`partitionIndex = (nextIndex++) % numberOfSubtasks` - **特点**: - 绝对均匀的数据分布(无论数据特征) - 消除数据倾斜问题[^1][^3] - **适用场景**:无Key数据或Key分布不均匀时(如文件读取) #### 3. 底层分区器实现对比 | 分区器 | 函数调用 | 负载均衡原理 | 适用场景 | |-----------------------|------------------|---------------------------|----------------------------| | `HashPartitioner` | `keyBy()` | 基于Key哈希值的模运算 | Key分布均匀的聚合操作 | | `RebalancePartitioner`| `rebalance()` | 顺序轮询分配 | 消除数据倾斜 | | `RescalePartitioner` | `rescale()` | 本地轮询(TaskManager内) | 减少跨节点通信 | #### 4. 负载均衡效果验证 通过Flink Web UI可监控子任务负载: 1. **均匀负载**:各子任务的`Records Sent`指标接近 2. **倾斜检测**:`BackPressure`标签显示高负载子任务 3. **吞吐量监控**:`numRecordsInPerSecond`波动小于10%[^1] > ⚠️ **注意事项**: > - 当`keyBy()`出现数据倾斜时(如热点Key),需结合`rebalance()`或自定义`Partitioner`[^3] > - 动态扩缩容时,`rebalance()`可自动重分布数据,而`keyBy()`需手动干预[^1] --- ### 相关问题 1. 如何通过自定义`Partitioner`解决Flink的数据倾斜问题? 2. `keyBy()`与`rebalance()`的性能差异在哪些场景下最显著?[^1][^3] 3. Flink的`RescalePartitioner`如何优化本地化数据传输? 4. 如何监控和诊断Flink任务中的负载不均衡现象? > 引用说明: > [^1]: Flink分区策略的性能与扩展性特性 > [^3]: 分区器与负载均衡的关联机制 > [^4]: 哈希分区器的底层实现原理 > [^5]: Key分配与子任务映射关系
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值