在 TableAPI 或者 SQL 要使用基于时间的操作,需要定义相关的时间语义和时 间数据来源的信息。所以,Table 可以提供一个额外的逻辑上的时间字段,用于 在表处理程序中,指示时间和访问相应的时间戳。
时间属性,可以是每个表 schema 的一部分。一旦定义了时间属性,它就 可以作为一个字段引用,并且可以在基于时间的操作中使用。时间属性的行为类 似于常规时间戳,可以访问,并且进行计算。
处理时间语义下,允许表处理程序根据机器的本地时间生成结果。它是时间 的最简单概念。它既不需要提取时间戳,也不需要生成 watermark。 定义处理时间属性有两种方法:
val bsEnv = StreamExecutionEnvironment.getExecutionEnvironment
bsEnv.setParallelism(1)
val bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
val bsTableEnv = StreamTableEnvironment.create(bsEnv, bsSettings)
val dataStream: DataStream[TrainAlarm] = bsEnv.readTextFile("D:\\Note\\Projects\\02\\Flink\\cha01\\file\\train2.txt")
.map(line => {
val splits = line.split(",")
TrainAlarm(splits(0), splits(1).toLong, splits(2).toDouble)
})
1.在 DataStream 转化为table时直接指定
♥♥注意:只能在 schema 定义的末尾来定义Processing Time
val table1 = bsTableEnv.fromDataStream(dataStream,'id,'ts,'temp,'pt.proctime())
2.在创建表 的 DDL 中指定
val table2: TableResult = bsTableEnv.executeSql(
"""
create table t_person(
name string,
ts bigint,
salary bigint,
city string,
pt AS PROCTIME()
) with (
'connector' = 'filesystem',
'path'='file:///D:\Note\Projects\02\Flink\cha01\file\flink_person.txt',
'format' = 'csv'
)
""".stripMargin)
在DataStream转换为Table的时候指定
val dataStream: DataStream[TrainAlarm] = bsEnv.readTextFile("D:\\Note\\Projects\\02\\Flink\\cha01\\file\\train2.txt")
.map(line => {
val splits = line.split(",")
TrainAlarm(splits(0), splits(1).toLong, splits(2).toDouble)
})
.assignTimestampsAndWatermarks(
WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(2))
.withTimestampAssigner(new SerializableTimestampAssigner[TrainAlarm] {
override def extractTimestamp(element: TrainAlarm, recordTimestamp: Long): Long = {
element.ts * 1000L
}
})
)
val table1 = bsTableEnv.fromDataStream(dataStream,'id,'ts.rowtime(),'temp)
在创建表的时候指定
val table2: TableResult = bsTableEnv.executeSql(
"""
create table t_person(
name string,
ts bigint,
salary bigint,
city string,
rt AS TO_TIMESTAMP( FROM_UNIXTIME(ts) ),
WATERMARK FOR rt AS rt - INTERVAL '1' SECOND
) with (
'connector' = 'filesystem',
'path'='file:///D:\Note\Projects\02\Flink\cha01\file\flink_person.txt',
'format' = 'csv'
)
""".stripMargin)
在 Table API 和 SQL 中,主要有两种窗口:Group Windows 和 Over Windows
分组窗口(Group Windows)会根据时间或行数间隔,将行聚合到有限的组 (Group) 中,并对每个组的数据执行一次聚合函数。
Table API 中的 Group Windows 都是使用.window子句定 义的,并且必须由 as 子句指定一个别名。为了按窗口对表进行分组,窗口的别名必须在 group by 子句中,像常规的分组字段一样引用。
val table = input
.window([w: GroupWindow] as 'w) // 定义窗口,别名 w
.groupBy('w, 'a) // 以属性 a 和窗口 w 作为分组的 key
.select('a, 'b.sum) // 聚合字段 b 的值,求和
滚动窗口(Tumbling windows)要用 Tumble 类来定义,另外还有三个方法:
over:定义窗口长度
on:用来分组(按时间间隔)或者排序(按行数)的时间字段
as:别名,必须出现在后面的 groupBy 中
// Tumbling Event-time Window(事件时间字段 rowtime)
.window(Tumble over 10.minutes on 'rowtime as 'w)
// Tumbling Processing-time Window(处理时间字段 proctime)
.window(Tumble over 10.minutes on 'proctime as 'w)
// Tumbling Row-count Window (类似于计数窗口,按处理时间排序,10 行一 组)
.window(Tumble over 10.rows on 'proctime as 'w)
滑动窗口(Sliding windows)要用 Slide 类来定义,另外还有四个方法: over:定义窗口长度
every:定义滑动步长
on:用来分组(按时间间隔)或者排序(按行数)的时间字段
as:别名,必须出现在后面的 groupBy 中
// Sliding Event-time Window
.window(Slide over 10.minutes every 5.minutes on 'rowtime as 'w) // Sliding Processing-time window
.window(Slide over 10.minutes every 5.minutes on 'proctime as 'w) // Sliding Row-count window
.window(Slide over 10.rows every 5.rows on 'proctime as 'w)
会话窗口(Session windows)要用 Session 类来定义,另外还有三个方法:
withGap:会话时间间隔
on:用来分组(按时间间隔)或者排序(按行数)的时间字段
as:别名,必须出现在后面的 groupBy 中
// Session Event-time Window
.window(Session withGap 10.minutes on 'rowtime as 'w)
// Session Processing-time Window
.window(Session withGap 10.minutes on 'proctime as 'w)
案例:
val bsEnv = StreamExecutionEnvironment.getExecutionEnvironment
bsEnv.setParallelism(1)
val bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
val bsTableEnv = StreamTableEnvironment.create(bsEnv, bsSettings)
val dataStream: DataStream[TrainAlarm] = bsEnv.readTextFile("D:\\Note\\Projects\\02\\Flink\\cha01\\file\\train2.txt")
.map(line => {
val splits = line.split(",")
TrainAlarm(splits(0), splits(1).toLong, splits(2).toDouble)
})
.assignTimestampsAndWatermarks(
WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(2))
.withTimestampAssigner(new SerializableTimestampAssigner[TrainAlarm] {
override def extractTimestamp(element: TrainAlarm, recordTimestamp: Long): Long = {
element.ts * 1000L
}
})
)
val table1 = bsTableEnv.fromDataStream(dataStream,'id,'ts.rowtime().as("rt"),'temp)
.window(Tumble.over(10.seconds()).on('rt).as("w"))
//.window(Slide.over(10.seconds()).every(2.seconds()).on('rt).as("w"))
//.window(Session.withGap(10.seconds()).on('rt).as("w"))
.groupBy('id,'w)
.select('id,'id.count(),'w.end(),'temp.max())
bsTableEnv.toAppendStream[Row](table1).print()
bsEnv.execute()
Group Windows 在 SQL 查询的 Group BY 子句中定义。与使用常规 GROUP BY 子句的查询一样,使用 GROUP BY 子句的查询会计算每个组的单个结果行。
SQL 支持以下 Group 窗口函数:
TUMBLE(time_attr, interval) 定义一个滚动窗口,第一个参数是时间字段,第二个参数是窗口长度。
HOP(time_attr, interval, interval) 定义一个滑动窗口,第一个参数是时间字段,第二个参数是窗口滑动步长,第三个是窗口长度。
SESSION(time_attr, interval) 定义一个会话窗口,第一个参数是时间字段,第二个参数是窗口间隔(Gap)。
TUMBLE_START(time_attr, interval)
TUMBLE_END(time_attr, interval)
TUMBLE_ROWTIME(time_attr, interval)
TUMBLE_PROCTIME(time_attr, interval)
案例:
val table2: TableResult = bsTableEnv.executeSql(
"""
create table t_person(
name string,
ts bigint,
salary bigint,
city string,
rt AS TO_TIMESTAMP( FROM_UNIXTIME(ts) ),
WATERMARK FOR rt AS rt - INTERVAL '1' SECOND
) with (
'connector' = 'filesystem',
'path'='file:///D:\\Note\\Projects\\02\\Flink\\cha01\\file\\flink_person.txt',
'format' = 'csv'
)
""".stripMargin)
val sqlResult = bsTableEnv.sqlQuery(
"""
select
name,
count(name) cnts,
tumble_end(rt,interval '10' second) win_end,
max(salary)
from t_person
group by name,tumble(rt,interval '10' second)
""".stripMargin)
bsTableEnv.toAppendStream[Row](sqlResult).print()
bsEnv.execute()
Over window 聚合是标准 SQL 中已有的(Over 子句),可以在查询的 SELECT 子句中定义。 Over window 聚合,会针对每个输入行,计算相邻行范围内的聚 合。Over windows 使用.window子句定义,并在 select() 方法中通过别名来引用。
val bsEnv = StreamExecutionEnvironment.getExecutionEnvironment
bsEnv.setParallelism(1)
val bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
val bsTableEnv = StreamTableEnvironment.create(bsEnv, bsSettings)
val dataStream: DataStream[TrainAlarm] = bsEnv.readTextFile("D:\\Note\\Projects\\02\\Flink\\cha01\\file\\train2.txt")
.map(line => {
val splits = line.split(",")
TrainAlarm(splits(0), splits(1).toLong, splits(2).toDouble)
})
.assignTimestampsAndWatermarks(
WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(2))
.withTimestampAssigner(new SerializableTimestampAssigner[TrainAlarm] {
override def extractTimestamp(element: TrainAlarm, recordTimestamp: Long): Long = {
element.ts * 1000L
}
})
)
val table1 = bsTableEnv.fromDataStream(dataStream,'id,'ts.rowtime().as("rt"),'temp)
.window(Over.partitionBy('id).orderBy('rt).preceding(2.rows).as("ow"))
.select('id,'temp.avg() over 'ow)
bsTableEnv.toAppendStream[Row](table1).print()
bsEnv.execute()
val table2: TableResult = bsTableEnv.executeSql(
"""
create table t_person(
name string,
ts bigint,
salary bigint,
city string,
rt AS TO_TIMESTAMP( FROM_UNIXTIME(ts) ),
WATERMARK FOR rt AS rt - INTERVAL '1' SECOND
) with (
'connector' = 'filesystem',
'path'='file:///D:\\Note\\Projects\\02\\Flink\\cha01\\file\\flink_person.txt',
'format' = 'csv'
)
""".stripMargin)
val sqlResult = bsTableEnv.sqlQuery(
"""
select
name,
avg(salary) over ow,
max(salary) over ow
from t_person
window ow as (
partition by name
order by rt
rows between 2 preceding and current row
)
""".stripMargin)
bsTableEnv.toAppendStream[Row](sqlResult).print()
bsEnv.execute()