Flink时间属性和窗口

基于时间的操作,需要定义相关的时间语义和时间数据来源的信息。在Table API和SQL中,会给表单独提供一个逻辑上的时间字段,专门用来在表处理程序中指示时间

时间属性是每个表模式结构的一部分,它可以在创建表DDL里直接定义为一个字段,也可以在DataStream转换成表时,一旦定义了时间属性,就可以作为一个普通字段引用,并且可以在基于时间的操作中使用。

时间属性的数据类型为TIMESTAMP,它的行为类似于常规时间戳,可以直接访问并且进行计算

按照时间语义的不同,可把时间属性的定义分成:事件时间、处理时间

文章目录

      • 事件时间
        • 1、在创建表的DDL中定义
        • 2、在数据流转换为表时定义
      • 处理时间
        • 1、在创建表DDL中定义
        • 2、在数据流转换为表时定义
      • 窗口
        • 1、分组窗口
        • 2、窗口表值函数

事件时间

在实际应用中,最常用的就是事件时间,在事件时间语义下,允许表处理程序根据每个数据中包含的时间戳来生成结果。

事件时间语义最大的用途就是处理乱序事件或者延迟事件的场景,通过设置水位线(watermark)来表示事件时间的进展,而水位线可以根据数据的最大时间戳设置一个延迟时间,这样即使在出现乱序的情况下,对数据的处理也可以获得正确结果

为了处理无序事件,并区分流中的迟到事件,Flink需要从事件数据中提取时间戳,并生成水位线,用来推进事件时间的进展

1、在创建表的DDL中定义

在创建表的DDL(CREATE TABLE语句)中,可以增加一个字段,通过WATERMARK语句来定义事件时间属性。WATERMARK语句主要用来定义水位线的生成表达式,这个表达式会将带有事件时间戳的字段标记为事件时间属性,在其基础上给出水位线的延迟时间

CREATE TABLE EventTable(
 user STRING,
 url STRING,
 ts TIMESTAMP(3),
 WATERMARK FOR ts AS ts - INTERVAL '5' SECOND
) WITH (...);

ts字段定义为事件时间属性,基于ts设置5秒的水位线延迟。这里的5秒是以时间间隔的形式定义的,格式是INTERVAL<数值><时间单位>

INTERVAL '5' SECOND

Flink中支持的事件时间属性数据类型必须为TIMESTAMP或者TIMESTAMP_LTZ。这里TIMESTAMP_LTZ是指带有本地时区信息的时间戳(TIMESTAMP WITH LOCAL TIME ZONE);一般情况下如果数据中的时间戳是“年-月-日-时-分-秒”的形式,就是不带时区信息的,可以将事件时间属性定义为TIMESTAMP类型

如果原始的时间戳是一个长整型的毫秒数,这时就需要另外定义一个字段来表示事件时间熟悉,类型定义为TIMESTAMP_LTZ更方便

CREATE TABLE events (
 user STRING,
 url STRING,
 ts BIGINT,
 ts_ltz AS TO_TIMESTAMP_LTZ(ts, 3),
 WATERMARK FOR ts_ltz AS time_ltz - INTERVAL '5' SECOND
) WITH (...);

这里另外定义了一个字段 ts_ltz,是把长整型的 ts 转换为 TIMESTAMP_LTZ 得到的;进而使用 WATERMARK 语句将它设为事件时间属性,并设置 5 秒的水位线延迟

2、在数据流转换为表时定义

事件时间属性也可以在将 DataStream 转换为表的时候来定义,调用fromDataStream()方法创建表时,追加参数来定义表中的字段结构,给某个字段加上rowtime()后缀,就表示将当前字段指定为事件时间属性。这个字段也可以是额外追加上去的逻辑字段,也可以是本身固有的字段,那么这个字段会被事件时间属性所覆盖,类型也会被转换为TIMESTAMP

这种方式只负责指定的时间属性,而时间戳的提取和水位线的生成应该之前就在DataStream上定义好了,由于DataStream中没有时区概念,因此Flink会将事件时间属性解析成不带时区的TIMESTAMP类型,所有的时间值都被当做UTC标准时间

// 方法一:
// 流中数据类型为二元组 Tuple2,包含两个字段;需要自定义提取时间戳并生成水位线
DataStream<Tuple2<String, String>> stream = inputStream.assignTimestampsAndWatermarks(...);
// 声明一个额外的逻辑字段作为事件时间属性
Table table = tEnv.fromDataStream(stream, $("user"), $("url"),$("ts").rowtime());
// 方法二:
// 流中数据类型为三元组 Tuple3,最后一个字段就是事件时间戳
DataStream<Tuple3<String, String, Long>> stream = inputStream.assignTimestampsAndWatermarks(...);
// 不再声明额外字段,直接用最后一个字段作为事件时间属性
Table table = tEnv.fromDataStream(stream, $("user"), $("url"),$("ts").rowtime());

处理时间

系统时间,使用时不需要额外提取时间戳和生成水位线。因此在定义处理时间属性时,必须额外声明一个字段用来保存当前的处理时间

1、在创建表DDL中定义

在创建表的DDL(CREATE TABLE 语句)中,可以增加一个额外字段,通过调用系统内置的PROCTIME()函数来指定当前的处理时间属性,返回的类型是TIMESTAMP_LTZ

CREATE TABLE EventTable(
 user STRING,
 url STRING,
 ts AS PROCTIME()
) WITH (...);
2、在数据流转换为表时定义

处理时间属性同样可以在将DataStream转化为表的时候来定义,我们调用fromDataStream()方法创建表时,可用proctime()后缀来指定处理时间属性字段。由于处理时间是系统时间,原始数据中并没有这个字段,故在处理时间属性一定不能定义在一个已有字段上,只能定义在表结构所有字段的最后,作为额外的逻辑字段出现

DataStream<Tuple2<String, String>> stream = ...;
// 声明一个额外的字段作为处理时间属性字段
Table table = tEnv.fromDataStream(stream, $("user"), $("url"),$("ts").proctime());

窗口

窗口可以无界流切割成大小有限的桶来做计算,通过截取有限数据集来处理无限的数据,在DataStream API中提供了对不同类型的窗口进行定义和处理接口

1、分组窗口

Table API和SQL提供了一组分组窗口函数,常用的时间窗口如滚动窗口、滑动窗口、会话窗口都有对应的实现。具体在SQL中就是调用TUMBLE()、HOP()、SESSION(),传入时间属性字段、窗口大小等参数,以滚动窗口为例:

TUMBLE(ts, INTERVAL '1' HOUR)

进行窗口计算时,分组窗口是窗口本身当做一个字段对数据进行分组,可对组内的数据进行聚合,基本使用方式如下:

Table result = tableEnv.sqlQuery(
 "SELECT " +
     "user, " +
    "TUMBLE_END(ts, INTERVAL '1' HOUR) as endT, " +
     "COUNT(url) AS cnt " +
     "FROM EventTable " +
     "GROUP BY " + // 使用窗口和用户名进行分组
     "user, " +
    "TUMBLE(ts, INTERVAL '1' HOUR)" // 定义 1 小时滚动窗口
 );
2、窗口表值函数

自1.13版本开始,Flink开始使用窗口表值函数(Windowing table-valued functions,Windowing TVFs)来定义窗口。窗口表值函数是Flink定义的多态表函数(PTF),可以将表扩展后返回,表函数可以看作是返回一个表的函数。

目前Flink提供的几个窗口TVF:滚动窗口、滑动窗口、累加窗口、会话窗口

窗口表值函数可以完全替代传统的分组窗口函数,窗口TVF更符合SQL标准,性能得到了优化,拥有更强大的功能,可以支持基于窗口的复杂计算,例如窗口Top-N、窗口联结等

在窗口TVF的返回值中,除去原始表中的所有列,还增加了用来描述窗口的额外3个列:窗口起始点(window_start)、窗口结束点(window_end)、窗口时间(window_time)

窗口时间的值 = window_end - 1ms,相当于窗口中能够包含数据的最大时间戳

(1)滚动窗口

滚动窗口在SQL中的概念与DataStream API中的定义完全一样,是长度固定、时间对齐、无重叠的窗口,一般用于周期性的统计计算

声明如下:

TUMBLE(TABLE EventTable, DESCRIPTOR(ts), INTERVAL '1' HOUR)

(2)滑动窗口

传入表名、时间属性、窗口大小、滑动步长

HOP(TABLE EventTable, DESCRIPTOR(ts), INTERVAL '5' MINUTES, INTERVAL '1' HOURS));

(3)累加窗口

滚动窗口和滑动窗口,可以用来计算大多数周期性的统计指标。不过在实际应用中还会遇到这样一类需求:我们的统计周期可能较长,因此希望中间每隔一段时间就输出一次当前的统计值;与滑动窗口不同的是,在一个统计周期内,我们会多次输出统计值,它们应该是不断叠加累积的

例子:按天来统计网站的PV,如果用1天的滚动窗口,那需要每天24点才会计算一次,输出频率太低;如果用滑动窗口,计算频率变高,统计的变成过去24小时的PV。若我们希望按照日统计每天的PV,不过需要每隔1小时就输出一次当天到目前为止的PV值,这种特殊的窗口就叫做累加窗口

Flink时间属性和窗口_第1张图片

累加窗口时窗口TVF中新增的窗口功能,会在一定的统计周期内进行累加计算。累加窗口中的两个核心参数:

  • 最大窗口长度:统计周期,统计这段时间内的数据
  • 累积步长:创建的第一个窗口大小为步长step,之后的每个窗口都会在之前的基础上再扩展step的长度,直到达到最大窗口长度
CUMULATE(TABLE EventTable, DESCRIPTOR(ts), INTERVAL '1' HOURS, INTERVAL '1' DAYS))

上述基于时间属性ts,在表EventTable上定义了一个统计周期为1天、累加步长为1小时的累加窗口。

你可能感兴趣的:(Flink,flink)