大数据-120 - Flink滑动窗口(Sliding Window)详解:原理、应用场景与实现示例 基于时间驱动&基于事件驱动

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

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

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

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

目前2025年10月07日更新到: Java-141 深入浅出 MySQL Spring事务失效的常见场景与解决方案详解(3) MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!

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

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

章节内容

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

  • Flink Window 背景总览
  • Flink Window 滚动时间窗口
  • 基于时间驱动
  • 基于事件驱动

滑动时间窗口

滑动窗口是固定窗口更广义的一种形式,它通过引入滑动间隔实现了窗口的动态移动。滑动窗口由固定的窗口长度(window size)和滑动间隔(slide interval)两个关键参数组成,其中窗口长度决定了每个窗口包含的数据范围,而滑动间隔则控制着窗口的移动频率。

Flink 的滑动时间窗口(Sliding Window)是一种特别适用于流式数据处理场景的窗口机制。它能够在持续流动的数据流中,按照设定的时间范围定期进行数据计算和聚合。这种窗口机制广泛应用于实时监控、异常检测、趋势分析等需要对数据进行周期性分析的场景。例如,在电商平台中,可以使用窗口长度为1小时、滑动间隔为5分钟的滑动窗口来实时统计近一小时的销售额变化趋势。

具体实现时,滑动窗口会按照指定的窗口大小和滑动步长不断地在数据流上滑动。比如设置窗口大小为10分钟、滑动步长为5分钟,那么每个窗口会包含10分钟的数据,但每隔5分钟就会产生一个新的窗口。这样的设计使得相邻窗口之间存在5分钟的重叠数据,从而确保计算结果的连续性。对于每个窗口内的数据,Flink都会独立执行预定义的聚合函数(如sum、count、avg等)进行计算。

与固定窗口相比,滑动窗口的优势在于它能够提供更细粒度的时间覆盖,避免重要数据被窗口边界分割。但同时也会带来更高的计算开销,因为相同的数据可能会被多个窗口重复计算。在实际应用中,需要根据业务需求和数据特点,在计算精度和性能开销之间找到合适的平衡点。

类型特点

窗口长度固定,可以有重叠。滑动窗口是一种常用的数据处理模式,具有以下特点:

  1. 窗口重叠机制

    • 滑动窗口会有重叠部分,因此每个事件可能会被包含在多个窗口中
    • 重叠程度由窗口大小和滑动步长决定。例如:
      • 窗口大小为5分钟,滑动步长为1分钟,则重叠部分为4分钟
      • 窗口大小为1小时,滑动步长为30分钟,则重叠部分为30分钟
  2. 典型应用场景

    • 滑动窗口更适合定期计算某个时间范围内的聚合值,像是:
      • 移动平均值(如股票5日均线)
      • 最近一段时间的活跃用户统计(如过去24小时内活跃用户数)
      • 实时流量监控(如每分钟请求量)
      • 传感器数据平滑处理(如温度传感器5秒均值)
  3. 实现方式

    • 基于时间:按固定时间间隔滑动(如每1分钟计算一次过去5分钟的数据)
    • 基于事件数量:每收到N个事件就计算一次最近M个事件的数据
    • 混合模式:同时考虑时间和事件数量触发计算
  4. 计算特点

    • 增量计算:可以复用重叠部分的计算结果,提高效率
    • 边界处理:需要考虑窗口边界事件的归属问题
    • 状态管理:需要维护窗口状态,特别是长窗口的场景
  5. 优势比较

    • 相比滚动窗口(无重叠),能提供更平滑的数据变化趋势
    • 相比会话窗口(基于事件间隔),能保证定期输出计算结果
    • 特别适合需要连续监控和实时响应的应用场景

关键参数

窗口大小(window size)

窗口大小定义了每个时间窗口所包含的数据范围,是流处理中最重要的参数之一。它表示系统处理数据时划分的时间段长度,通常以时间单位(秒、分钟、小时)表示。例如:

  • 10秒窗口:系统每10秒收集的数据作为一个处理单元
  • 1分钟窗口:每分钟的数据作为一个批次处理

窗口大小直接影响:

  1. 数据处理延迟:窗口越大,延迟越高
  2. 结果精确度:窗口越小,结果越实时
  3. 系统负载:窗口越小,计算频率越高

滑动步长(slide interval)

滑动步长决定了窗口移动的频率,即系统创建新窗口的时间间隔。这个参数控制着:

  • 窗口重叠程度:当步长小于窗口大小时,会产生窗口重叠
  • 计算触发频率:步长越小,计算触发越频繁

典型配置示例:

  1. 10秒窗口+5秒步长:

    • 窗口1:00:00-00:10
    • 窗口2:00:05-00:15
    • 窗口3:00:10-00:20
    • 这样每5秒就会产生一个包含10秒数据的新窗口
  2. 1分钟窗口+30秒步长:

    • 窗口1:00:00-01:00
    • 窗口2:00:30-01:30
    • 窗口3:01:00-02:00

应用场景:

  • 实时监控系统:5秒窗口+1秒步长,实现准实时的监控报警
  • 每日统计报表:24小时窗口+24小时步长,每天生成一次完整报表
  • 用户行为分析:30分钟窗口+5分钟步长,平衡实时性和计算开销

基于时间驱动

场景:我们可以每30秒计算一次最近一分钟用户购买的商品数

java 复制代码
package icu.wzk;
public class SlidingWindow {

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        DataStreamSource<String> dataStreamSource = env.socketTextStream("localhost", 9999);
        SingleOutputStreamOperator<Tuple2<String, Integer>> mapStream = dataStreamSource
                .map(new MapFunction<String, Tuple2<String, Integer>>() {
                    @Override
                    public Tuple2<String, Integer> map(String value) throws Exception {
                        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        long timeMillis = System.currentTimeMillis();
                        int random = new Random().nextInt(10);
                        System.out.println("value: " + value + ", random: " + random +
                                ", timestamp: " + format.format(timeMillis));
                        return Tuple2.of(value, random);
                    }
                });
        KeyedStream<Tuple2<String, Integer>, Tuple> keyedStream = mapStream
                .keyBy(new KeySelector<Tuple2<String, Integer>, Tuple>() {
                    @Override
                    public Tuple getKey(Tuple2<String, Integer> value) throws Exception {
                        return Tuple1.of(value.f0);
                    }
                });
        WindowedStream<Tuple2<String, Integer>, Tuple, TimeWindow> timeWindow = keyedStream
                .timeWindow(Time.seconds(10), Time.seconds(5));
        timeWindow.apply(new MyTimeWindowFunction()).print();
        
        env.execute("SlidingWindow");
    }

}

基于事件驱动

java 复制代码
package icu.wzk;
public class SlidingWindow {

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        DataStreamSource<String> dataStreamSource = env.socketTextStream("localhost", 9999);
        SingleOutputStreamOperator<Tuple2<String, Integer>> mapStream = dataStreamSource
                .map(new MapFunction<String, Tuple2<String, Integer>>() {
                    @Override
                    public Tuple2<String, Integer> map(String value) throws Exception {
                        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        long timeMillis = System.currentTimeMillis();
                        int random = new Random().nextInt(10);
                        System.out.println("value: " + value + ", random: " + random +
                                ", timestamp: " + format.format(timeMillis));
                        return Tuple2.of(value, random);
                    }
                });
        KeyedStream<Tuple2<String, Integer>, Tuple> keyedStream = mapStream
                .keyBy(new KeySelector<Tuple2<String, Integer>, Tuple>() {
                    @Override
                    public Tuple getKey(Tuple2<String, Integer> value) throws Exception {
                        return Tuple1.of(value.f0);
                    }
                });
        WindowedStream<Tuple2<String, Integer>, Tuple, GlobalWindow> globalWindow = keyedStream
                .countWindow(3, 2);
        globalWindow.apply(new MyCountWindowFuntion()).print();
        
        env.execute("SlidingWindow");
    }

}

会话窗口

由一系列事件组合一个指定时间长度timeout间隙组成,类似于Web应用的Session,也就是一段时间没有接收到新数据会生成新的窗口。 Session窗口分配器通过Session活动来对元素进行分组,Session窗口跟滚动窗口和滑动窗口相比,不会有重叠和固定的开始时间和结束时间的情况。 Session窗口在一个固定的时间周期内不再收到元素,即非活动间隔产生,那么这个窗口就会关闭。 一个Session窗口通过一个Session间隔来配置,这个Session间隔定义了非活跃周期的长度,当这个非活跃周期产生,那么当前的Session将关闭并且后续的元素将被分配到新的Session窗口去。

类型特点

会话窗口(Session Window)是一种特殊的时间窗口类型,具有以下具体特点:

  1. 非重叠性

    • 会话窗口之间完全独立,不会出现时间重叠
    • 每个窗口都是单独存在的个体
    • 不同于翻滚窗口的固定分割和滑动窗口的重叠特性
  2. 动态时间边界

    • 没有预设的固定开始和结束时间
    • 窗口的开启由第一个到达的元素触发
    • 窗口的关闭取决于配置的超时时间(Session Gap)
  3. 基于活动的窗口机制

    • 当超过配置的会话间隙(Session Gap)时间没有新元素到达时,当前会话窗口会自动关闭
    • 例如:如果设置Session Gap为5分钟,当超过5分钟没有新数据,当前窗口就会结束
  4. 自适应分割

    • 后续到达的元素会触发新会话窗口的创建
    • 每个新窗口都独立于之前的窗口
    • 窗口持续时间完全取决于数据到达模式

应用场景示例:

  • 用户行为分析:将用户连续的操作记录作为一个会话
  • 网络会话管理:TCP连接的开始和结束
  • 电商场景:用户的一次完整购物流程

技术实现特点:

  • 需要维护每个键(key)的最新活动时间戳
  • 需要定期检查超时的窗口
  • 适合处理不规律到达的数据流

与常规窗口的对比:

  • 翻滚窗口:固定大小,固定间隔
  • 滑动窗口:固定大小,可重叠
  • 会话窗口:动态大小,不重叠

基于时间驱动

java 复制代码
package icu.wzk;
public class SessionWindow {

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        DataStreamSource<String> dataStreamSource = env.socketTextStream("localhost", 9999);
        SingleOutputStreamOperator<Tuple2<String, Integer>> mapStream = dataStreamSource
                .map(new MapFunction<String, Tuple2<String, Integer>>() {
                    @Override
                    public Tuple2<String, Integer> map(String value) throws Exception {

                        return null;
                    }
                });
        KeyedStream<Tuple2<String, Integer>, Tuple> keyedStream = mapStream
                .keyBy(new KeySelector<Tuple2<String, Integer>, Tuple>() {
                    @Override
                    public Tuple getKey(Tuple2<String, Integer> value) throws Exception {
                        return Tuple1.of(value.f0);
                    }
                });
        WindowedStream<Tuple2<String, Integer>, Tuple, TimeWindow> window = keyedStream
                .window(ProcessingTimeSessionWindows.withGap(Time.seconds(10)));
        window.apply(new MyTimeWindowFunction()).print();
        env.execute("SessionWindow");
    }

}
相关推荐
用户281113022216 小时前
分布式事务总结
后端
Hello.Reader6 小时前
Flink 广播状态(Broadcast State)实战从原理到落地
java·大数据·flink
ApacheSeaTunnel6 小时前
从小时级到分钟级:多点DMALL如何用Apache SeaTunnel把数据集成成本砍到1/3?
大数据·开源·数据集成·seatunnel·技术分享
数据要素X6 小时前
寻梦数据空间 | 路径篇:从概念验证到规模运营的“诊-规-建-运”实施指南
大数据·人工智能·数据要素·数据资产·可信数据空间
big-data16 小时前
Paimon系列:主键表流读之changelog producer
大数据
xuejianxinokok6 小时前
新版本 python 3.14 性能到底如何?
后端·python
Ray666 小时前
代理模式
后端
考虑考虑6 小时前
Jpa中的枚举类型
spring boot·后端·spring
peter5276 小时前
LangChain4j入门使用
后端