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

七、总结

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

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

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

相关推荐
jkyy201422 分钟前
AI运动数字化:以技术重塑场景,健康有益赋能全域运动健康管理
大数据·人工智能·健康医疗
金融小师妹29 分钟前
4月30日多因子共振节点:鲍威尔“收官效应”与权力结构重塑的预期重构
大数据·人工智能·重构·逻辑回归
2601_9499251834 分钟前
AI Agent如何重构跨境物流的决策?
大数据·人工智能·重构·ai agent·geo优化·物流科技
xiaoduo AI1 小时前
客服机器人问题解决率怎么统计?Agent系统自动判断是否解决,比人工回访准?
大数据·人工智能·机器人
小五兄弟2 小时前
YouTube 肖像检测扩展背后:短剧出海版权保护的技术实现与实战策略
大数据·人工智能
阿瑞说项目管理3 小时前
2026 实战入门指南:企业 Agent 到底能解决哪些工作问题?
大数据·人工智能·agent·智能体·企业级ai
ZOOOOOOU3 小时前
云边端协同架构下,门禁权限引擎的离线决策与策略续存实现
大数据·人工智能·架构
189228048613 小时前
EMMC32G-TA28闪存EMMCH26M78103CCR
大数据·人工智能·缓存
dingzd953 小时前
Facebook强化原创内容分发后跨境品牌如何重做素材策略
大数据·人工智能·新媒体运营·内容营销·跨境
Mr_sst4 小时前
infra-ai模块宏观设计解析:业务与模型之间的中间层核心架构
大数据·人工智能·ai·llama