滚动窗口、滑动窗口及会话窗口
/**
* (1)滚动窗口
*
* 滚动窗口(Tumbling windows)要用 Tumble 类来定义, 另外还有三个方法:
* ⚫ over:定义窗口长度
* ⚫ on:用来分组(按时间间隔) 或者排序(按行数) 的时间字段
* ⚫ as:别名,必须出现在后面的 groupBy 中
*/
// Tumbling Event-time Window (事件时间字段rowtime)
table1.window(Tumble over 10.minutes on 'rowtime as 'w)
// Tumbling Processing-time Window (处理时间字段rowtime)
table1.window(Tumble over 10.minutes on 'proctime as 'w)
// Tumbling Row-count Window (类似于计数窗口, 按处理时间排序, 10 行一组)
table1.window(Tumble over 10.rows on 'proctime as 'w)
/**
* (2)滑动窗口
*
* 滑动窗口(Sliding windows)要用 Slide 类来定义,另外还有四个方法:
* ⚫ over:定义窗口长度
* ⚫ every:定义滑动步长
* ⚫ on:用来分组(按时间间隔)或者排序(按行数)的时间字段
* ⚫ as:别名,必须出现在后面的 groupBy 中
*/
// Sliding Event-time Window
table1.window(Slide over 10.minutes every 5.minutes on 'rowtime as 'w)
// Sliding Processing-time window
table1.window(Slide over 10.minutes every 5.minutes on 'proctime as 'w)
// Sliding Row-count window
table1.window(Slide over 10.rows every 5.rows on 'proctime as 'w)
/**
* (3)会话窗口
*
* 会话窗口(Session windows)要用 Session 类来定义, 另外还有三个方法:
* ⚫ withGap:会话时间间隔
* ⚫ on:用来分组(按时间间隔)或者排序(按行数)的时间字段
* ⚫ as:别名,必须出现在后面的 groupBy 中
*/
// Session Event-time Window
table1.window(Session withGap 10.minutes on 'rowtime as 'w)
// Session Processing-time Window
table1.window(Session withGap 10.minutes on 'proctime as 'w)
无界的 over window及有界的 over window
/**
* 1) 无界的 over window
*/
// 无界的事件时间 over window (时间字段 "rowtime")
table1.window(Over partitionBy 'a orderBy 'rowtime preceding UNBOUNDED_RANGE as 'w)
//无界的处理时间 over window (时间字段"proctime")
table1.window(Over partitionBy 'a orderBy 'proctime preceding UNBOUNDED_RANGE as 'w)
// 无界的事件时间 Row-count over window (时间字段 "rowtime")
table1.window(Over partitionBy 'a orderBy 'rowtime preceding UNBOUNDED_ROW as 'w)
//无界的处理时间 Row-count over window (时间字段 "rowtime")
table1.window(Over partitionBy 'a orderBy 'proctime preceding UNBOUNDED_ROW as 'w)
/**
* 2) 有界的 over window
*/
// 有界的事件时间 over window (时间字段 "rowtime", 之前 1 分钟)
table1.window(Over partitionBy 'a orderBy 'rowtime preceding 1.minutes as 'w)
// 有界的处理时间 over window (时间字段 "rowtime", 之前 1 分钟)
table1.window(Over partitionBy 'a orderBy 'proctime preceding 1.minutes as 'w)
// 有界的事件时间 Row-count over window (时间字段 "rowtime", 之前 10 行)
table1.window(Over partitionBy 'a orderBy 'rowtime preceding 10.rows as 'w)
// 有界的处理时间 Row-count over window (时间字段 "rowtime", 之前 10 行)
table1.window(Over partitionBy 'a orderBy 'proctime preceding 10.rows as 'w)
/** 我们已经了解了在 Table API 里 window 的调用方式, 同样,我们也可以在 SQL 中直接加入窗口的定义和使用。 */
(1)Group Windows
SQL 支持以下 Group 窗口函数:
⚫ TUMBLE(time_attr, interval)
定义一个滚动窗口,第一个参数是时间字段,第二个参数是窗口长度。
⚫ HOP(time_attr, interval, interval)
定义一个滑动窗口,第一个参数是时间字段,第二个参数是窗口滑动步长,第三个是窗口长度。
⚫ SESSION(time_attr, interval)
定义一个会话窗口,第一个参数是时间字段,第二个参数是窗口间隔(Gap)。
另外还有一些辅助函数, 可以用来选择 Group Window 的开始和结束时间戳, 以及时间属性。
这里只写 TUMBLE_*,滑动和会话窗口是类似的(HOP_*, SESSION_*)。
⚫ TUMBLE_START(time_attr, interval)
⚫ TUMBLE_END(time_attr, interval)
⚫ TUMBLE_ROWTIME(time_attr, interval)
⚫ TUMBLE_PROCTIME(time_attr, interval)
(2)Over Windows
由于 Over 本来就是 SQL 内置支持的语法,所以这在 SQL 中属于基本的聚合操作。所有聚合必须在同一窗口上定义,
也就是说, 必须是相同的分区、排序和范围。 目前仅支持在当前行范围之前的窗口(无边界和有边界)。注意, ORDER BY 必须在单一的时间属性上指定。
SELECT
COUNT(amount) OVER ( PARTITION BY user ORDER BY proctime ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
FROM
Orders
// 也可以做多个聚合
SELECT
COUNT(amount) OVER w, SUM(amount) OVER w
FROM Orders
WINDOW w AS (PARTITION BY user ORDER BY proctime ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
案例
滚动窗口,统计 10 秒内出现的每个 sensor 的个数。
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.table.api.{EnvironmentSettings, Table, Tumble}
import org.apache.flink.table.api.scala._
/**
* 可以开一个滚动窗口,统计 10 秒内出现的每个 sensor 的个数。
*/
object _05_FlinkTest {
def main(args: Array[String]): Unit = {
// 创建流的执行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
// 读取本地文件
val dataFrame: DataStream[String] = env.readTextFile("C:\\Users\\user\\Desktop\\数据仓库理论\\flink_sql\\代码\\FlinkTutorial\\src\\main\\resources\\sensor.txt")
// map成样例类,然后提取水位线
val ds: DataStream[_02_SensorReading] = dataFrame
.map(
data => {
val arr: Array[String] = data.split(",")
_02_SensorReading(arr(0), arr(1).toLong, arr(2).toDouble) }
)
.assignTimestampsAndWatermarks(
new BoundedOutOfOrdernessTimestampExtractor[_02_SensorReading](Time.seconds(1)) {
override def extractTimestamp(t: _02_SensorReading): Long = t.timestamp * 1000L
})
// 创建表的执行环境
val settings: EnvironmentSettings = EnvironmentSettings.newInstance()
.useBlinkPlanner()
.inStreamingMode()
.build()
val tabEnv: StreamTableEnvironment = StreamTableEnvironment.create(env, settings)
// 将DataStream转换为Table,并指定时间字段
val table: Table = tabEnv.fromDataStream(ds,'id,'temperature,'timestamp.rowtime)
// 滚动窗口,统计 10 秒内出现的每个 sensor 的个数
/**
* 第一种:flink table api
*/
val resTable1: Table = table
.window(Tumble over 10.seconds on 'timestamp as 'tw)
.groupBy('id, 'tw)
.select('id, 'id.count)
/**
* 第二种:flink sql
*/
val sqlTable: Table = table
.select('id, 'temperature, 'timestamp as 'ts)
// TUMBLE(time_attr, interval)
// 定义一个滚动窗口,第一个参数是时间字段,第二个参数是窗口长度。
val resTable2: Table = tabEnv.sqlQuery(
"select id,count(id) from " + sqlTable + " group by id,tumble(ts,interval '10' second) ")
// resultTable 转换为数据流
val resDs1: DataStream[(Boolean, (String, Long))] = resTable1.toRetractStream[(String, Long)]
val resDs2: DataStream[(Boolean, (String, Long))] = resTable2.toRetractStream[(String, Long)]
resDs1.filter(_._1).print("resDs1 ---> ")
resDs2.filter(_._1).print("resDs2 ---> ")
// 执行程序
env.execute()
}
}