FlinkSql中窗口(window)的使用

FlinkSql中窗口(window)的使用

时间语义,要配合窗口操作才能发挥作用。最主要的用途,当然就是开窗口然后根据时间段做计算了。

在Table API和SQL中,主要有两种窗口:Group Windows和Over Windows

一、Table API中使用窗口

Group Windows

分组窗口(Group Windows)会根据时间或行计数间隔,将行聚合到有限的组(Group)中,并对每个组的数据执行一次聚合函数
Table API中的Group Windows都是使用.window(w:GroupWindow)子句定义的,并且必须由as子句指定一个别名。为了按窗口对表进行分组,窗口的别名必须在group by子句中,像常规的分组字段一样引用。

  • 滚动窗口
public class Flink08_TableApi_Window_1 {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        SingleOutputStreamOperator<WaterSensor> waterSensorStream = env
            .fromElements(new WaterSensor("sensor_1", 1000L, 10),
                          new WaterSensor("sensor_1", 2000L, 20),
                          new WaterSensor("sensor_2", 3000L, 30),
                          new WaterSensor("sensor_1", 4000L, 40),
                          new WaterSensor("sensor_1", 5000L, 50),
                          new WaterSensor("sensor_2", 6000L, 60))
            .assignTimestampsAndWatermarks(
                WatermarkStrategy
                    .<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(5))
                    .withTimestampAssigner((element, recordTimestamp) -> element.getTs())
            );

        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
        Table table = tableEnv
            .fromDataStream(waterSensorStream, $("id"), $("ts").rowtime(), $("vc"));

        table
            .window(Tumble.over(lit(10).second()).on($("ts")).as("w"))  // 定义滚动窗口并给窗口起一个别名
            .groupBy($("id"), $("w")) // 窗口必须出现的分组字段中
            .select($("id"), $("w").start(), $("w").end(), $("vc").sum())
            .execute()
            .print();

        env.execute();
    }
}
  • 滑动窗口
.window(Slide.over(lit(10).second()).every(lit(5).second()).on($("ts")).as("w"))
  • 会话窗口
.window(Session.withGap(lit(6).second()).on($("ts")).as("w")

Over Windows

Over window聚合是标准SQL中已有的(Over子句),可以在查询的SELECT子句中定义。Over window 聚合,会针对每个输入行,计算相邻行范围内的聚合。

Table API提供了Over类,来配置Over窗口的属性。可以在事件时间或处理时间,以及指定为时间间隔、或行计数的范围内,定义Over windows。

无界的over window是使用常量指定的。也就是说,时间间隔要指定UNBOUNDED_RANGE,或者行计数间隔要指定UNBOUNDED_ROW。而有界的over window是用间隔的大小指定的。

  • Unbounded Over Windows
public class Flink09_TableApi_OverWindow_1 {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        SingleOutputStreamOperator<WaterSensor> waterSensorStream = env
            .fromElements(new WaterSensor("sensor_1", 1000L, 10),
                          new WaterSensor("sensor_1", 4000L, 40),
                          new WaterSensor("sensor_1", 2000L, 20),
                          new WaterSensor("sensor_2", 3000L, 30),
                          new WaterSensor("sensor_1", 5000L, 50),
                          new WaterSensor("sensor_2", 6000L, 60))
            .assignTimestampsAndWatermarks(
                WatermarkStrategy
                    .<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(1))
                    .withTimestampAssigner((element, recordTimestamp) -> element.getTs())
            );

        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
        Table table = tableEnv
            .fromDataStream(waterSensorStream, $("id"), $("ts").rowtime(), $("vc"));

        table
            .window(Over.partitionBy($("id")).orderBy($("ts")).preceding(UNBOUNDED_ROW).as("w"))
            .select($("id"), $("ts"), $("vc").sum().over($("w")).as("sum_vc"))
            .execute()
            .print();

        env.execute();
    }
}


# 使用UNBOUNDED_RANGE
.window(Over.partitionBy($("id")).orderBy($("ts")).preceding(UNBOUNDED_RANGE).as("w"))

说明:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • Bounded Over Windows
// 当事件时间向前算3s得到一个窗口
.window(Over.partitionBy($("id")).orderBy($("ts")).preceding(lit(3).second()).as("w"))
// 当行向前推算2行算一个窗口
.window(Over.partitionBy($("id")).orderBy($("ts")).preceding(rowInterval(2L)).as("w"))

二、SQL API中使用窗口

Group Windows

SQL 查询的分组窗口是通过 GROUP BY子句定义的。类似于使用常规 GROUP BY 语句的查询,窗口分组语句的 GROUP BY 子句中带有一个窗口函数为每个分组计算出一个结果。以下是批处理表和流处理表支持的分组窗口函数:

分组窗口函数描述
TUMBLE(time_attr, interval)定义一个滚动窗口。滚动窗口把行分配到有固定持续时间( interval )的不重叠的连续窗口。比如,5 分钟的滚动窗口以 5 分钟为间隔对行进行分组。滚动窗口可以定义在事件时间(批处理、流处理)或处理时间(流处理)上。
HOP(time_attr, interval, interval)定义一个跳跃的时间窗口(在 Table API 中称为滑动窗口)。滑动窗口有一个固定的持续时间( 第二个 interval 参数 )以及一个滑动的间隔(第一个 interval 参数 )。若滑动间隔小于窗口的持续时间,滑动窗口则会出现重叠;因此,行将会被分配到多个窗口中。比如,一个大小为 15 分组的滑动窗口,其滑动间隔为 5 分钟,将会把每一行数据分配到 3 个 15 分钟的窗口中。滑动窗口可以定义在事件时间(批处理、流处理)或处理时间(流处理)上。
SESSION(time_attr, interval)定义一个会话时间窗口。会话时间窗口没有一个固定的持续时间,但是它们的边界会根据 interval 所定义的不活跃时间所确定;即一个会话时间窗口在定义的间隔时间内没有时间出现,该窗口会被关闭。例如时间窗口的间隔时间是 30 分钟,当其不活跃的时间达到30分钟后,若观测到新的记录,则会启动一个新的会话时间窗口(否则该行数据会被添加到当前的窗口),且若在 30 分钟内没有观测到新纪录,这个窗口将会被关闭。会话时间窗口可以使用事件时间(批处理、流处理)或处理时间(流处理)。

在这里插入图片描述

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
// 作为事件时间的字段必须是 timestamp 类型, 所以根据 long 类型的 ts 计算出来一个 t
tEnv.executeSql("create table sensor(" +
                    "id string," +
                    "ts bigint," +
                    "vc int, " +
                    "t as to_timestamp(from_unixtime(ts/1000,'yyyy-MM-dd HH:mm:ss'))," +
                    "watermark for t as t - interval '5' second)" +
                    "with("
                    + "'connector' = 'filesystem',"
                    + "'path' = 'input/sensor.txt',"
                    + "'format' = 'csv'"
                    + ")");

tEnv
    .sqlQuery(
        "SELECT id, " +
            "  TUMBLE_START(t, INTERVAL '1' minute) as wStart,  " +
            "  TUMBLE_END(t, INTERVAL '1' minute) as wEnd,  " +
            "  SUM(vc) sum_vc " +
            "FROM sensor " +
            "GROUP BY TUMBLE(t, INTERVAL '1' minute), id"
    )
    .execute()
    .print();
tEnv
    .sqlQuery(
        "SELECT id, " +
            "  hop_start(t, INTERVAL '1' minute, INTERVAL '1' hour) as wStart,  " +
            "  hop_end(t, INTERVAL '1' minute, INTERVAL '1' hour) as wEnd,  " +
            "  SUM(vc) sum_vc " +
            "FROM sensor " +
            "GROUP BY hop(t, INTERVAL '1' minute, INTERVAL '1' hour), id"
    )
    .execute()
    .print();

Over Windows

tEnv
    .sqlQuery(
        "select " +
            "id," +
            "vc," +
            "sum(vc) over(partition by id order by t rows between 1 PRECEDING and current row)" +
            "from sensor"
    )
    .execute()
    .print();

tEnv
    .sqlQuery(
        "select " +
            "id," +
            "vc," +
            "count(vc) over w, " +
            "sum(vc) over w " +
            "from sensor " +
            "window w as (partition by id order by t rows between 1 PRECEDING and current row)"
    )
    .execute()
    .print();
<think>好的,我现在需要帮助用户了解Flink SQL窗口函数的使用教程和示例。首先,我要回忆一下Flink SQL窗口函数相关知识,并参考用户提供的四个引用内容来确保信息的准确性。 用户的问题是关于Flink SQL窗口函数的使用,所以我需要先确定主要的窗口类型。根据引用[2]和[4],Flink SQL支持滚动窗口(TUMBLE)、滑动窗口(HOP)和会话窗口(SESSION)。此外,还有计数窗口(CountWindow)和自定义窗口的提及,但用户可能更关注时间相关的窗口函数,尤其是基于事件时间和处理时间的区别。 接下来,我需要为每种窗口函数提供具体的语法和示例。例如,滚动窗口的基本语法结构,以及如何指定事件时间或处理时间。根据引用[2],处理时间可以使用PROCTIME()函数,而事件时间需要指定时间属性和水印。同时,引用[3]提到表函数的扩展,可能涉及到自定义函数,但用户的问题可能更关注内置窗口函数。 然后,我需要考虑如何组织这些信息。可能的结构是先介绍窗口函数的类型,再分别详细说明每种窗口的语法、参数和使用场景,最后给出实际应用示例。例如,滚动窗口适合固定时间间隔的聚合,会话窗口则根据活动间隔划分,而滑动窗口允许重叠的时间段。 在编写示例时,需要确保SQL语句的正确性,比如在定义表时指定时间属性,使用正确的窗口函数语法,并展示完整的查询语句。例如,滚动窗口的示例应包括事件时间和处理时间的两种情况,并展示聚合操作。引用[4]中提到了会话窗口的GAP参数,这需要在示例中明确说明。 另外,用户可能需要了解窗口函数在实际应用中的案例。例如,如何统计每小时的PV/UV,或用户行为分析中的会话划分。引用[1]和[3]提到Flink Table API和SQL的基本用法,可能需要结合这些内容来展示如何创建表和查询。 还需要注意窗口函数与其他函数的结合使用,比如聚合函数(SUM、COUNT)以及可能需要的过滤或分组操作。同时,处理时间和事件时间的区别需要明确,避免混淆。引用[2]指出TUMBLE和SESSION窗口通常用于事件时间,但也可以用于处理时间,这一点需要强调。 最后,生成相关问题,帮助用户进一步探索。可能的问题包括窗口函数的底层原理、处理时间与事件时间的性能差异、动态调整会话间隔的方法等,这些都能引导用户深入理解。 需要确保回答结构清晰,分步骤解释,并引用提供的参考文献来支持内容。例如,在讨论处理时间时引用[2],在提到表函数时引用[3],在会话窗口部分引用[4]。同时,代码示例要正确使用语法,比如时间属性的定义和窗口函数的参数。 总结步骤: 1. 介绍Flink SQL支持的窗口类型。 2. 分述每种窗口函数的语法、参数和示例。 3. 提供实际应用场景和综合案例。 4. 强调处理时间与事件时间的区别。 5. 生成相关问题供用户进一步学习。 现在需要将以上思考整理成用户友好的回答,确保符合格式要求,正确使用LaTeX,引用标记,并提供相关问题和示例代码。</think>### Flink SQL窗口函数使用教程及示例 #### 一、窗口函数类型及语法 Flink SQL支持以下三类核心时间窗口函数(基于引用[2][4]): 1. **滚动窗口(TUMBLE)** - **语法**:`TUMBLE(TABLE 表名, DESCRIPTOR(时间字段), 窗口大小)` - **特点**:窗口无重叠,固定时间间隔(如每小时统计一次)。 - **时间类型**:支持事件时间(需定义水印)或处理时间(通过`PROCTIME()`获取[^2])。 2. **滑动窗口(HOP)** - **语法**:`HOP(TABLE 表名, DESCRIPTOR(时间字段), 滑动步长, 窗口大小)` - **特点**:窗口可重叠,需定义窗口长度和滑动间隔(如每5分钟统计最近15分钟数据)。 3. **会话窗口(SESSION)** - **语法**:`SESSION(TABLE 表名, DESCRIPTOR(时间字段), 会话间隔)` - **特点**:基于数据活跃间隔划分窗口,若超过指定间隔无数据则关闭窗口[^4]。 #### 二、使用示例 ##### 1. 滚动窗口(事件时间) **场景**:统计每小时订单金额总和。 ```sql -- 定义表结构(事件时间) CREATE TABLE orders ( order_id STRING, amount DOUBLE, order_time TIMESTAMP(3), WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND ); -- 查询每小时交易额 SELECT TUMBLE_START(order_time, INTERVAL '1' HOUR) AS window_start, SUM(amount) AS total_amount FROM TABLE(TUMBLE(TABLE orders, DESCRIPTOR(order_time), INTERVAL '1' HOUR)) GROUP BY TUMBLE(order_time, INTERVAL '1' HOUR); ``` ##### 2. 滑动窗口(处理时间) **场景**:每5分钟统计最近15分钟的PV。 ```sql -- 定义处理时间字段(通过计算列) CREATE TABLE user_clicks ( user_id STRING, click_time AS PROCTIME() ); -- 查询滑动窗口统计 SELECT HOP_START(click_time, INTERVAL '5' MINUTE, INTERVAL '15' MINUTE) AS window_start, COUNT(*) AS pv FROM TABLE(HOP(TABLE user_clicks, DESCRIPTOR(click_time), INTERVAL '5' MINUTE, INTERVAL '15' MINUTE)) GROUP BY HOP(click_time, INTERVAL '5' MINUTE, INTERVAL '15' MINUTE); ``` ##### 3. 会话窗口(动态间隔) **场景**:用户行为分析,30秒无活动则关闭会话。 ```sql SELECT SESSION_START(order_time, INTERVAL '30' SECOND) AS session_start, COUNT(DISTINCT user_id) AS active_users FROM TABLE(SESSION(TABLE orders, DESCRIPTOR(order_time), INTERVAL '30' SECOND)) GROUP BY SESSION(order_time, INTERVAL '30' SECOND); ``` #### 三、关键注意事项 1. **时间属性定义** - 事件时间必须通过`WATERMARK`声明,而处理时间可用`PROCTIME()`自动生成[^2]。 2. **窗口函数返回值** - 使用`TUMBLE_START`/`TUMBLE_END`等函数获取窗口起止时间。 3. **聚合操作** - 窗口函数需与`GROUP BY`结合,且聚合字段必须包含窗口函数表达式。 #### 四、综合应用案例 **需求**:实时统计每用户最近1小时的消费金额(滑动窗口,每10分钟更新)。 ```sql SELECT user_id, HOP_START(order_time, INTERVAL '10' MINUTE, INTERVAL '1' HOUR) AS window_start, SUM(amount) AS total_spent FROM TABLE(HOP(TABLE orders, DESCRIPTOR(order_time), INTERVAL '10' MINUTE, INTERVAL '1' HOUR)) GROUP BY user_id, HOP(order_time, INTERVAL '10' MINUTE, INTERVAL '1' HOUR); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值