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)
又要快响应又要丰富输出 混合模式

七、总结

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

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

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

相关推荐
财迅通Ai1 天前
6000万吨产能承压 卫星化学迎来战略窗口期
大数据·人工智能·物联网·卫星化学
武子康1 天前
大数据-263 实时数仓-Canal 增量订阅与消费原理:MySQL Binlog 数据同步实践
大数据·hadoop·后端
LJ97951111 天前
媒体发布新武器:Infoseek融媒体平台使用指南
大数据·人工智能
科技小花1 天前
AI重塑数据治理:2026年核心方案评估与场景适配
大数据·人工智能·云原生·ai原生
方向研究1 天前
存储芯片生产
大数据
代码青铜1 天前
如何用 Zion 实现 AI 图片分析与电商文案自动生成流程
大数据·人工智能
gaoshengdainzi1 天前
GB/T23448-2019卫生洁具软管专用检测设备全套解决方案
大数据·卫生洁具软管检测设备·软管试验机
茶靡花开04151 天前
什么是DMS经销商管理系统?经销商管理系统哪个好?
大数据·人工智能
Gofarlic_OMS1 天前
HyperWorks用户仿真行为分析与许可证资源分点配置
java·大数据·运维·服务器·人工智能
fire-flyer1 天前
ClickHouse系列(二):MergeTree 家族详解
大数据·数据库·clickhouse