大数据-128 - Flink 并行度详解:从概念到最佳实践,一文读懂任务并行执行机制 代码示例与性能优化

点一下关注吧!!!非常感谢!!持续更新!!!

🚀 AI篇持续更新中!(长期更新)

AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布!"快的模型 + 深度思考模型 + 实时路由",持续打造实用AI工具指南!📐🤖

💻 Java篇正式开启!(300篇)

目前2025年10月13日更新到: Java-147 深入浅出 MongoDB 分页查询详解:skip() + limit() + sort() 实现高效分页、性能优化与 WriteConcern 写入机制全解析 MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈! 大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

章节内容

上节我们完成了如下的内容:

  • ManageOperatorState
  • StateBackend
  • Checkpoint

并行度的基本概念

在 Apache Flink 中,并行度(Parallelism)是指每个算子(Operator)在执行时可以同时运行的并行任务(Task)数量。Flink 程序的数据流图由多个算子组成(包括 Source、Transformation 和 Sink),每个算子都可以独立设置其并行度。

并行度的重要性

并行度是 Flink 性能调优的关键参数之一:

  • 决定了任务在集群中的资源使用情况
  • 直接影响数据处理吞吐量
  • 影响作业的容错能力和恢复速度

并行度的组成结构

Flink 程序的执行模型可以分解为:

  1. Job: 完整的 Flink 程序
  2. Operator: 数据处理的基本单元(如 map、filter、join 等)
  3. Task: 算子并行执行的实例
  4. Subtask: 实际的执行单元(线程)

并行度的设置方式

Flink 提供了多种灵活的方式来设置并行度:

1. 全局并行度设置

java 复制代码
// 在 ExecutionConfig 中设置
env.setParallelism(4);
  • 影响整个作业中所有算子的默认并行度
  • 通常在作业入口处设置

2. 算子级别并行度

java 复制代码
DataStream<String> data = env.addSource(new CustomSource())
    .map(new MyMapper()).setParallelism(2)
    .filter(new MyFilter()).setParallelism(3);
  • 可以针对特定算子设置与全局不同的并行度
  • 适用于计算密集型或I/O密集型的特殊算子

3. 配置文件设置

flink-conf.yaml 中:

yaml 复制代码
parallelism.default: 3
  • 作为集群的默认并行度
  • 当代码中未明确设置时使用此值

4. 客户端提交参数

bash 复制代码
./bin/flink run -p 5 -c com.example.MyJob myJob.jar
  • 在使用 CLI 提交作业时指定
  • 会覆盖代码中的全局并行度设置

并行度设置的最佳实践

考虑因素

  1. 数据量大小:大数据量通常需要更高并行度
  2. 算子特性:像 window 这样的有状态算子可能需要特殊考虑
  3. 集群资源:不超过可用 TaskManager 的 slot 总数
  4. 数据倾斜:可能需要调整并行度来平衡负载

典型场景示例

  • ETL 管道:Source 和 Sink 并行度通常与外部系统分区数匹配
  • 流式分析:关键算子可能需要 2-4 倍 CPU 核心数的并行度
  • 批处理作业:可设置较高并行度以加快处理速度

并行度与资源管理

Flink 的并行度设置需要与集群资源配置协调:

  • 每个 TaskManager 提供固定数量的 slot
  • 每个 slot 可以运行一个并行任务
  • 总并行度不应超过集群 slot 总数

例如,如果集群有 3 个 TaskManager,每个有 4 个 slot,那么最大有效并行度为 12。超过此值的设置会导致部分任务无法启动。

通过合理设置并行度,可以最大化利用集群资源,提高 Flink 作业的处理效率和吞吐量。

并行度的概念

在 Flink 中,并行度主要决定每个操作符在作业中被分配多少个并发实例来处理数据。操作符的并行实例越多,任务就能够越快完成。通常,Flink 作业中的每个操作符都会以并行实例的形式执行在集群中的不同 TaskManager 上,这样可以充分利用集群的计算资源。 Flink 中的并行度可以分为以下几个层级:

全局并行度(Global Parallelism)

全局并行度是指 Flink 集群默认为所有作业和操作符分配的并行度。在配置文件 flink-conf.yaml 中,你可以通过以下配置来设置 Flink 集群的默认全局并行度:

shell 复制代码
parallelism.default: 4

这个配置将为每个没有指定并行度的操作符分配默认的 4 个并行实例。如果你没有在代码中或任务提交时明确设置并行度,Flink 将使用这个默认值。

作业并行度(Job-level Parallelism)

在提交 Flink 作业时,你可以为整个作业设置并行度,覆盖全局默认值。例如,在命令行使用 flink run 提交作业时可以通过 -p 参数来设置并行度:

shell 复制代码
flink run -p 10 your-job.jar

此命令将作业的并行度设置为 10,作业中的每个操作符都会被分配 10 个并行实例。这个设置的优先级高于全局并行度。

算子并行度(Operator-level Parallelism)

你可以在代码中为每个具体的算子设置不同的并行度。Flink 提供了灵活的算子级别并行度控制,可以根据数据处理逻辑的需要对不同的算子设定不同的并行度。例如:

shell 复制代码
DataStream<String> stream = env.readTextFile("input.txt")
                               .map(new MyMapper())
                               .setParallelism(5);

在这段代码中,map 操作的并行度被设置为 5,这意味着 map 操作会启动 5 个并发任务来处理数据。其他没有显式设置并行度的算子将使用默认的作业级别并行度。

Slot 并行度(Slot-level Parallelism)

Flink 中的 TaskManager 是执行并行任务的工作节点,每个 TaskManager 中可以包含多个任务槽(Slot)。每个 Slot 对应一个并发任务实例,并可以同时运行多个任务实例。Slot 并行度是 Flink 资源分配中的重要概念,如果作业的并行度超过了集群中可用的 Slot 数,Flink 会进行资源调度,这可能会导致性能下降。 每个 TaskManager 可以配置 Slot 数,例如:

shell 复制代码
taskmanager.numberOfTaskSlots: 4

Operator Level

算子级别,一个算子,数据源和Sink并行度可以通过调用setParalleism()方法来指定

shell 复制代码
actions.filter(new FilterFunction<UserAction>() {
    @Override
    public boolean filter(UserAction value) throws Exception {
        return false;
    }
}).setParallelism(4);

Execution Environment Level

Env级别

执行环境(任务)的默认并行度可以通过调用setParallelism()方法指定,为了并行度3来执行所有的算子、数据源的DataSink,可以通过如下的方式设置执行环境的并行度:

shell 复制代码
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(4);

Client Level

客户端级别,推荐使用。 并行度可在客户端将Job提交到Flink时设定,对于CLI客户端,可以通过-p参数指定并行度。

System Level

系统默认级别,尽量不使用。在系统级可以通过设置 flink-conf.yaml中的parallism.default属性来执行环境的默认并行度。

Flink 提供了几种方法来设置并行度:

shell 复制代码
parallelism.default: 4

在提交作业时设置并行度

这里的 -p 20 设置作业的并行度为 20。

shell 复制代码
flink run -p 20 your-job.jar

在代码中为算子设置并行度

shell 复制代码
DataStream<String> dataStream = env.readTextFile("input.txt")
                                  .map(new MyMapper())
                                  .setParallelism(10);

并行度的优化策略

合理设置并行度可以有效提高 Flink 作业的性能,但并行度的设置需要根据数据量、任务复杂度、集群资源等多个因素综合考虑。以下是一些优化策略:

根据数据量设置合理的并行度

对于大数据量的任务,可以通过增加并行度来提高处理速度,但并不是并行度越高越好。过高的并行度会导致资源浪费和任务调度开销。一般来说,建议作业的并行度不要超过 TaskManager 可用 Slot 的总数。

合理分配操作符的并行度

某些操作符,比如 keyBy() 后的 reduce 或 aggregate,其并行度受键值数量的限制,因此为这些操作符设置过高的并行度并不会提高性能。你可以通过数据的特性和操作符的逻辑来合理分配不同操作符的并行度。

利用资源监控进行动态调优

在任务运行时,可以使用 Flink 的 Web UI 来监控作业的运行状态。如果发现某些算子的处理速度慢、资源利用率低,可以考虑调整这些算子的并行度。此外,Flink 允许通过 REST API 或 Web UI 动态调整并行度,而无需重新提交作业。

考虑网络和 I/O 限制

Flink 作业的性能不仅取决于 CPU 和内存,还受限于网络带宽和 I/O 速度。在处理大数据时,如果作业需要频繁地进行网络传输或者 I/O 操作(如读取和写入 HDFS、Kafka),应避免过高的并行度导致网络或磁盘 I/O 的瓶颈。

并行度与容错性

Flink 支持容错机制,当任务失败时,Flink 会根据保存点(checkpoint)进行恢复。高并行度的作业通常会生成更多的 checkpoint 数据,在某些情况下会增加作业恢复时的开销。因此,在设置高并行度时,要同时考虑到 Flink 容错机制可能带来的性能影响。

代码实例

假设有一个作业需要从 Kafka 读取数据,经过 map 转换后将处理结果写入 HDFS。在这种场景下,你可以根据任务的负载和集群资源设置不同算子的并行度: 假设我们有一个 Flink 作业,该作业的任务是:

  • 从 Kafka 读取实时的交易数据流。
  • 对每一条交易数据进行清洗和转换。
  • 将清洗后的数据写入 HDFS 进行存储。 这个任务需要根据各个操作的特性设置不同的并行度,以实现性能和资源的最佳利用。
java 复制代码
public class FlinkParallelismExample {

    public static void main(String[] args) throws Exception {
        // 1. 创建流处理环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        
        // 设置全局并行度(默认并行度)
        env.setParallelism(8);  // 全局默认并行度为8

        // 2. 配置 Kafka 消费者
        Properties kafkaProps = new Properties();
        kafkaProps.setProperty("bootstrap.servers", "localhost:9092");
        kafkaProps.setProperty("group.id", "transaction-group");

        FlinkKafkaConsumer<String> kafkaSource = new FlinkKafkaConsumer<>(
                "transaction-topic", new SimpleStringSchema(), kafkaProps);

        // 设置 Kafka 源的并行度
        DataStream<String> transactionStream = env
                .addSource(kafkaSource)
                .setParallelism(6); // 从 Kafka 读取数据时的并行度为 6

        // 3. 数据转换操作
        DataStream<String> cleanedData = transactionStream
                .map(value -> cleanTransactionData(value))
                .setParallelism(12); // 数据清洗的并行度为 12

        // 4. 将清洗后的数据写入 HDFS
        cleanedData
                .writeAsText("hdfs://namenode:8020/flink/cleaned_transactions/")
                .setParallelism(4);  // 写入 HDFS 的并行度为 4

        // 5. 启动任务
        env.execute("Flink Parallelism Example");
    }

    // 数据清洗的逻辑
    public static String cleanTransactionData(String transaction) {
        // 假设清洗逻辑包括去除不必要的字段,格式化数据等
        return transaction.trim();  // 简单清洗逻辑示例
    }
}

代码说明

  • 全局并行度:我们在代码中通过 env.setParallelism(8) 设置了全局的并行度为 8。这意味着,除非显式设置,所有的算子默认都会使用 8 个并行实例运行。
  • Kafka 消费并行度:通过 setParallelism(6) 为从 Kafka 读取数据的操作设置了并行度为 6。也就是说,Flink 将会启动 6 个并行任务来从Kafka 的 transaction-topic 主题中消费数据。这个并行度可以根据 Kafka 分区的数量调整。如果 Kafka 有 6 个分区,那么设置并行度为 6 是合理的,这样可以保证每个分区都有一个并发实例进行处理。
  • 数据转换并行度:数据从 Kafka 读取后,进入 map 操作进行清洗和转换。这里的并行度被设置为 12(setParallelism(12)),即清洗任务将启动 12 个并行实例来同时处理数据。这可以提高数据处理速度,但也需要确保集群中有足够的计算资源支持这个并行度。
  • HDFS 写入并行度:在数据清洗完成后,将数据写入 HDFS 文件系统。这里我们设置了写入 HDFS 的并行度为 4(setParallelism(4))。这意味着将有 4 个并发任务负责将数据写入到 HDFS。由于 HDFS 的写入通常涉及磁盘 I/O 操作,设置较低的并行度可以避免 I/O 争用。

并行度优先级规则

Flink 任务的并行度设置遵循明确的优先级顺序:

  1. 算子级别 - 直接在代码中为特定算子设置(最高优先级)
  2. 环境级别 - 通过env.setParallelism()设置
  3. 客户端级别 - 提交任务时通过参数指定
  4. 系统默认级别 - 使用flink-conf.yaml中的默认配置(最低优先级)

并行度限制条件

  • Source 并行度限制 :如果数据源(如Kafka特定分区或单文件)不支持并行读取,即使设置了高并行度也不会生效 示例:读取单个CSV文件时,并行度只能为1

并行度调整注意事项

  1. Shuffle 影响:改变并行度会导致任务重新划分,可能引发数据Shuffle

    • 频繁调整会增加网络开销
    • 可能导致数据倾斜问题
  2. 推荐做法

    • 优先使用任务提交时动态指定(如-p参数)
    • 在生产环境中,建议通过-p参数统一控制

核心概念区分

  • Slot(槽位)
    • 静态资源概念
    • 代表TaskManager的实际并发执行能力
    • 配置方式:taskmanager.numberOfTaskSlots
  • Parallelism(并行度)
    • 动态执行概念
    • 表示程序运行时实际使用的并发数量
    • 可通过代码或参数动态调整

最佳实践建议

  1. 对于批处理作业,可在不同阶段设置不同并行度
  2. 流处理作业建议保持稳定的并行度设置
  3. 监控并行度调整后的资源使用情况
  4. 考虑下游系统的处理能力(如数据库连接池大小)
相关推荐
小毛驴8505 小时前
在Spring Boot开发中,HEAD、OPTIONS和 TRACE这些HTTP方法各有其特定的应用场景和实现方式
spring boot·后端·http
zl9798995 小时前
SpringBoot-依赖管理和自动配置
spring boot·后端·状态模式
JaguarJack5 小时前
PHP8.5 的新 URI 扩展
后端·php
绝无仅有5 小时前
面试真实经历某商银行大厂数据库MYSQL问题和答案总结(一)
后端·面试·github
绝无仅有5 小时前
Docker 实战经验之关键文件误删恢复指南
后端·面试·github
QZQ541885 小时前
go中reflect的底层原理
后端
白衣鸽子6 小时前
CAP理论:分布式系统的“不可能三角”
后端·架构
焰火19996 小时前
[Java]基于Spring的轻量级定时任务动态管理框架
java·后端
Hello.Reader6 小时前
Flink Data Source 理论与实践架构、时序一致性、容错恢复、吞吐建模与实现模式
架构·flink·linq