Flink 核心算子详解:map / flatMap / filter / process

文章目录

在学习 Flink 的过程中,map、flatMap、filter、process 是最常用、也是最容易让人迷糊的几个算子。

很多初学者都会有这些疑问:

  • 为什么 flatMap 里一定要写 Collector?
  • 为什么 map 不能返回多个元素?
  • process 到底强在哪里?什么时候该用?

本文将 从接口设计出发 ,结合 可运行 Demo + 实际运行结果,带你真正理解 Flink 算子的设计思想。


一、算子能力对照表

算子 输入 → 输出 是否可丢数据 是否可多输出 是否可用时间/状态
map 1 → 1 ❌ ❌ ❌
filter 1 → 0/1 ✅ ❌ ❌
flatMap 1 → 0/N ✅ ✅ ❌
process 1 → 0/N ✅ ✅ ✅

一句话总结:

越简单的算子,约束越多,Flink 能优化得越好;
越底层的算子,能力越强,但责任全在你。


二、测试数据

text 复制代码
hello flink
hello world

三、map:一进一出

1. 接口定义

java 复制代码
public interface MapFunction<T, R> {
    R map(T value) throws Exception;
}

特点:

  • 一个输入
  • 必须返回一个输出
  • 不能多、不能少

2. Demo:字符串转大写

java 复制代码
  StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStream<String> source = env.fromElements(
                "hello flink",
                "hello world"
        );

        source.map(value -> value.toUpperCase()).print();
        try {
            env.execute("Simple Map Example");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

DataStream 程序是惰性执行的,必须调用 execute() 才会触发作业执行

3. 运行结果

text 复制代码
8> HELLO WORLD
7> HELLO FLINK

四、filter:只负责"要不要"

1. 接口定义

java 复制代码
public interface FilterFunction<T> {
    boolean filter(T value) throws Exception;
}

注意:

  • 不能修改数据
  • 只能决定保留 or 丢弃

2. Demo:只保留包含 flink 的行

java 复制代码
source
    .filter(line -> line.contains("flink"))
    .print();

3. 运行结果

text 复制代码
6> hello flink

五、flatMap:一进多出

1. 接口定义

java 复制代码
public interface FlatMapFunction<T, O> {
    void flatMap(T value, Collector<O> out) throws Exception;
}

2. 为什么要 Collector?

因为:

flatMap 允许一条输入,输出 0 条、1 条或多条数据

返回值已经不够用,所以 Flink 把"输出控制权"交给你。


3. Demo:拆分单词

java 复制代码
        source.flatMap((String line, Collector<String> out) -> {
                    for (String word : line.split(" ")) {
                        out.collect(word);
                    }
                })
                .returns(Types.STRING)   // ⭐ 关键:补全类型信息
                .print();

4. 运行结果

text 复制代码
4> hello
4> world
3> hello
3> flink

六、process:最底层、最强大的算子

map / filter / flatMap 能做的,process 全都能做

并且还能:

  • 获取时间
  • 使用状态
  • 注册定时器

1. 接口结构

java 复制代码
public abstract class ProcessFunction<I, O> {
    public abstract void processElement(
        I value,
        Context ctx,
        Collector<O> out
    ) throws Exception;
}

2. Demo:手写 WordCount(不使用 keyBy.sum)

java 复制代码
      source
                .keyBy(value -> value)
                .process(new ProcessFunction<String, Tuple2<String, Integer>>() {

                    private int count = 0;

                    @Override
                    public void processElement(
                            String value,
                            Context ctx,
                            Collector<Tuple2<String, Integer>> out) {

                        count++;
                        out.collect(Tuple2.of(value, count));
                    }
                })
                .print();

3. 运行结果

text 复制代码
3> (hello flink,1)
6> (hello world,1)

⚠️ 注意:这里只是演示 process 能力

实际生产应使用 Keyed State 而不是普通成员变量


七、如何选择算子?

官方推荐原则:

能用 map,就别用 flatMap
能用 flatMap,就别用 process

原因是:

  • 简单算子 → Flink 能做更多优化
  • process → 灵活但难维护、难调优

八、总结

  • map:最简单,1 → 1
  • filter:只做判断
  • flatMap:拆分、多输出
  • process:终极武器,慎用

理解算子 ≠ 记 API
理解算子 = 理解接口设计 + 数据流模型

相关推荐
外收内放5 小时前
面向对象高级(实战案例)
开发语言·python·学习
专业程序开发源6 小时前
springboot欧缇蔓月子中心管理系统82697-计算机课程设计、毕业设计
java·spring boot·后端·python·spring·django·课程设计
FPGA小迷弟6 小时前
用 Claude Code 重构遗留系统:从评估到落地的完整实践指南
c++·python·ai·plotly·django·ai编程·claude
卷无止境6 小时前
面向arm64桌面客户端开发全景:语言、框架与生态
后端·python
fanruitian6 小时前
python else用法
开发语言·python
Data-Miner7 小时前
需要做财务数据分析,有什么好用的工具?从准备到出报告的实操指南
大数据·数据库·数据分析
Thinker QAQ7 小时前
并发编程(十一):Shared Memory 与 Message Passing——关系与组合使用
java·python·go·并发编程·shared memory·message passing
用户8356290780517 小时前
如何使用 Python 对 Excel 数据进行分组和分级显示
后端·python
vx_Biye_Design7 小时前
django房屋租赁推荐系统78455-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·课程设计·express
阿里云大数据AI技术8 小时前
StarRocks × Paimon C++:加速湖上数据分析
大数据·人工智能