Apache Flink 窗口处理函数全解析(增量 + 全量 + 混合)

Flink 窗口(Window) 是处理 无界流数据 时最核心的概念之一,它能将无限制的数据流按 时间或数量 切分成一个个有限的"数据桶",然后在这些"桶"里执行聚合计算。


一、什么是窗口处理函数

Flink 窗口处理函数定义了 窗口内数据如何被计算与输出。根据处理时机不同,可以分为:

类型 是否缓存窗口内所有数据 优点 典型函数
增量处理 低延迟、节省空间 reduceaggregate
全量处理 可访问全窗口数据 applyprocess

二、增量处理 --- 每条数据来就处理

🔹 reduce

  • 每条数据到达都会更新聚合结果
  • 输入 / 累加器 / 输出 类型一致
  • 不保存整个窗口数据,只累加状态
java 复制代码
.reduce(new ReduceFunction<SensorReading>() {
    @Override
    public SensorReading reduce(SensorReading a, SensorReading b) {
        // 计算最大温度
        return a.getTemperature() > b.getTemperature() ? a : b;
    }
})

⚠ 如果窗口只有一条数据,reduce() 不会被调用。


🔹 aggregate

更灵活的累加处理:

✔ 输入类型、累加器类型、输出类型可以不一致

✔ 可在累加器中做更复杂逻辑

核心方法:

  • createAccumulator():初始化累加器
  • add():每条记录到达调用
  • getResult():窗口触发时返回结果
  • merge():会话窗口需要合并状态

三、全量处理 --- 等窗口触发再处理

不同于增量处理,全量处理保留整个窗口数据,并在窗口结束时一次性计算:

🔹 apply

适合窗口中数据量不是特别大但需要全量访问的场景。

java 复制代码
.apply(new WindowFunction<...>() {
    @Override
    public void apply(...) {
        // 访问完整窗口数据操作
    }
});

🔹 process

最底层的全量处理函数,可以获取更多上下文信息,如窗口时间、watermark、状态等:

java 复制代码
.process(new ProcessWindowFunction<SensorReading, String, String, TimeWindow>() {
    @Override
    public void process(String key, Context context,
                        Iterable<SensorReading> elements,
                        Collector<String> out) {
        // 访问窗口全部元素
    }
});

这个函数比 apply 更强大。


四、为什么这些很重要?

  • 大部分真实业务中,我们既希望结果准确(全量处理),又希望响应快(增量处理)。
  • 通过增量处理先减少延迟,再用全量处理做更精细计算,可以在性能和准确性间取得平衡。

五、传感器温度实时窗口统计

事件模型 --- SensorReading

java 复制代码
public class SensorReading {
    private String sensorId;
    private Long timestamp;
    private Double temperature;

    public SensorReading() {}

    public SensorReading(String sensorId, Long timestamp, Double temperature) {
        this.sensorId = sensorId;
        this.timestamp = timestamp;
        this.temperature = temperature;
    }

    // getter / setter

    @Override
    public String toString() {
        return "SensorReading{" +
                "sensorId='" + sensorId + '\'' +
                ", timestamp=" + timestamp +
                ", temperature=" + temperature +
                '}';
    }
}

🔹 5.2 自定义模拟数据源 --- SensorSource

java 复制代码
public class SensorSource implements SourceFunction<SensorReading> {
    private volatile boolean running = true;

    @Override
    public void run(SourceContext<SensorReading> ctx) throws Exception {
        Random rand = new Random();

        while (running) {
            long timestamp = System.currentTimeMillis();
            // 5 个 sensor 并行发送
            for (int i = 0; i < 5; i++) {
                String sensorId = "sensor_" + i;
                double temp = 20 + rand.nextGaussian() * 10;
                ctx.collect(new SensorReading(sensorId, timestamp, temp));
            }
            Thread.sleep(200);
        }
    }

    @Override
    public void cancel() {
        running = false;
    }
}

主程序 --- 引入 Watermark + 多种窗口处理

java 复制代码
public class FlinkWindowDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStream<SensorReading> stream = env
                .addSource(new SensorSource())
                .assignTimestampsAndWatermarks(
                        WatermarkStrategy
                                .<SensorReading>forBoundedOutOfOrderness(Duration.ofSeconds(5))
                                .withTimestampAssigner((event, ts) -> event.getTimestamp())
                );

        // ------ 增量处理:reduce
        stream
                .keyBy(SensorReading::getSensorId)
                .window(TumblingEventTimeWindows.of(Duration.ofSeconds(10)))
                .reduce((a, b) -> a.getTemperature() > b.getTemperature() ? a : b)
                .print("Reduce Max Temp");

        // ------ 全量处理:process
        stream
                .keyBy(SensorReading::getSensorId)
                .window(TumblingEventTimeWindows.of(Duration.ofSeconds(10)))
                .process(new ProcessWindowFunction<SensorReading, String, String, TimeWindow>() {
                    @Override
                    public void process(String key, Context ctx,
                                        Iterable<SensorReading> elements,
                                        Collector<String> out) {
                        int count = 0;
                        double sum = 0;
                        for (SensorReading r : elements) {
                            count++;
                            sum += r.getTemperature();
                        }
                        out.collect(key + " avg=" + (sum / count) + ", count=" + count);
                    }
                })
                .print("Process Avg Temp");

        env.execute("Flink Window Demo");
    }
}

六、何时用哪种处理方式?

场景 推荐
实时性要求高 & 只需简单汇总 增量处理 (reduce, aggregate)
需要完整窗口统计 全量处理 (process, apply)
又要快响应又要丰富输出 混合模式

七、总结

✔ 增量处理 ------ 快、少空间,但无法访问全部数据

✔ 全量处理 ------ 能访问所有数据,结果丰富但占空间

✔ 混合模式 ------ 最灵活、兼顾性能和业务需求

相关推荐
C++、Java和Python的菜鸟4 小时前
第2章 项目前置课-代码版本控制Git
大数据·elasticsearch·搜索引擎
Sammyyyyy5 小时前
如何在不停项目不停机的情况下切换AI大模型
大数据·人工智能
circuitsosk5 小时前
跨境电商智能化实战:AI如何赋能客服自动回复、广告智能投放与供应链预测
大数据·人工智能·python·langchain·智能客服
数智化管理手记6 小时前
海量数据如何沉淀有效数据资产?数据标准化治理方案如何搭建?
java·大数据·人工智能
刀客Doc7 小时前
即时零售不只是多一个渠道,品牌开始重做终端生意
大数据·数据库
湘美书院--湘美谈教育7 小时前
湘美书院主理人谈AI文学:提示词与Skill的与时俱进
大数据·人工智能·安全·自动化·生活
weixin_549808367 小时前
人力资源数字化转型:从工具堆叠到AI原生架构的组织级跃迁路径
大数据·人工智能
l0001097 小时前
图书馆静谧环境构建:主流声学品牌产品与服务梳理
大数据·人工智能·声音
ACP广源盛139246256739 小时前
蚂蚁百灵 Ling‑3.0‑flash 开源 + 昇腾 0‑Day 原生适配@ACP#GSV9001E 在国产算力矩阵中的机会与落地场景
大数据·人工智能·分布式·单片机·嵌入式硬件
微三云-张梅10 小时前
东莞企业做GEO:AI信任体系的三个建设层级
大数据·人工智能·微三云geo·东莞系统开发·东莞geo