Flink基礎系列32-Table API和Flink SQL之窗口 一.窗口 二.案例 參考:

一.窗口

  時間語義,要配合窗口操作才能發揮作用。最主要的用途,當然就是開窗口、根據時間 段做計算了。下面我們就來看看 Table API 和 SQL 中,怎麼利用時間字段做窗口操作。

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

1.1 分組窗口(Group Windows)

  分組窗口(Group Windows)會根據時間或行計數間隔,將行聚合到有限的組(Group) 中,並對每個組的數據執行一次聚合函數。

  Table API 中的 Group Windows 都是使用.window(w:GroupWindow)子句定義的,並且 必須由 as 子句指定一個別名。爲了按窗口對錶進行分組,窗口的別名必須在 group by 子句 中,像常規的分組字段一樣引用。

Table table = input
.window([w: GroupWindow] as "w") // 定義窗口,別名 w
.groupBy("w, a")    // 以屬性 a 和窗口 w 作爲分組的 key
.select("a, b.sum") // 聚合字段 b 的值,求和

或者,還可以把窗口的相關信息,作爲字段添加到結果表中:

Table table = input
.window([w: GroupWindow] as "w")
.groupBy("w, a")
.select("a, w.start, w.end, w.rowtime, b.count")

  Table API 提供了一組具有特定語義的預定義 Window 類,這些類會被轉換爲底層
DataStream 或 DataSet 的窗口操作。

  Table API 支持的窗口定義,和我們熟悉的一樣,主要也是三種:滾動(Tumbling)、滑 動(Sliding)和會話(Session)。

1.1.1 滾動窗口

滾動窗口(Tumbling windows)要用 Tumble 類來定義,另外還有三個方法:
⚫ over:定義窗口長度
⚫ on:用來分組(按時間間隔)或者排序(按行數)的時間字段
⚫ as:別名,必須出現在後面的 groupBy 中

代碼如下:

// Tumbling Event-time Window
.window(Tumble.over("10.minutes").on("rowtime").as("w"))

// Tumbling Processing-time Window
.window(Tumble.over("10.minutes").on("proctime").as("w"))

// Tumbling Row-count Window
.window(Tumble.over("10.rows").on("proctime").as("w"))

1.1.2 滑動窗口

滑動窗口(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"))

1.1.3 會話窗口

會話窗口(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"))

1.2 Over Windows

  Over window 聚合是標準 SQL 中已有的(Over 子句),可以在查詢的 SELECT 子句中定義。Over window 聚合,會針對每個輸入行,計算相鄰行範圍內的聚合。Over windows
使用.window(w:overwindows*)子句定義,並在 select()方法中通過別名來引用。

比如這樣:

Table table = input
.window([w: OverWindow] as "w")
.select("a, b.sum over w, c.min over w")

Table API 提供了 Over 類,來配置 Over 窗口的屬性。可以在事件時間或處理時間,以及 指定爲時間間隔、或行計數的範圍內,定義 Over windows。

無界的 over window 是使用常量指定的。也就是說,時間間隔要指定 UNBOUNDED_RANGE, 或者行計數間隔要指定 UNBOUNDED_ROW。而有界的 over window 是用間隔的大小指定的。

實際代碼應用如下:

  1. 無界的 over window
// 無界的事件時間 over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding.(UNBOUNDED_RANGE).as("w"))

// 無界的處理時間 over window
.window(Over.partitionBy("a").orderBy("proctime").preceding.(UNBOUNDED_RANGE).as("w"))

// 無界的事件時間 Row-count over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding.(UNBOUNDED_ROW). as("w"))

//無界的處理時間 Row-count over window
.window(Over.partitionBy("a").orderBy("proctime").preceding.(UNBOUNDED_ROW).as("w"))
  1. 有界的 over window
// 有界的事件時間 over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding("1.minutes").as("w"))


// 有界的處理時間 over window
.window(Over.partitionBy("a").orderBy("proctime").preceding("1.minutes").as ("w"))

// 有界的事件時間 Row-count over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding("10.rows").as("w "))

// 有界的處理時間 Row-count over window
.window(Over.partitionBy("a").orderBy("procime").preceding("10.rows").as("w "))

1.3 SQL 中窗口的定義

  我們已經瞭解了在 Table API 裏 window 的調用方式,同樣,我們也可以在 SQL 中直接加入窗口的定義和使用。

1.3.1 Group Windows

  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)。

另外還有一些輔助函數,可以用來選擇 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)

1.3.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

二.案例

代碼:

package org.flink.tableapi;


import org.flink.beans.SensorReading;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.table.api.Over;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.Tumble;
import org.apache.flink.table.api.java.StreamTableEnvironment;
import org.apache.flink.types.Row;

/**
 * @author 只是甲
 * @date   2021-09-30
 */

public class TableTest5_TimeAndWindow {
    public static void main(String[] args) throws Exception {
        // 1. 創建環境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);

        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        // 2. 讀入文件數據,得到DataStream
        DataStream<String> inputStream = env.readTextFile("C:\\Users\\Administrator\\IdeaProjects\\FlinkStudy\\src\\main\\resources\\sensor.txt");

        // 3. 轉換成POJO
        DataStream<SensorReading> dataStream = inputStream.map(line -> {
            String[] fields = line.split(",");
            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
        })
                .assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<SensorReading>(Time.seconds(2)) {
                    @Override
                    public long extractTimestamp(SensorReading element) {
                        return element.getTimestamp() * 1000L;
                    }
                });

        // 4. 將流轉換成表,定義時間特性
//        Table dataTable = tableEnv.fromDataStream(dataStream, "id, timestamp as ts, temperature as temp, pt.proctime");
        Table dataTable = tableEnv.fromDataStream(dataStream, "id, timestamp as ts, temperature as temp, rt.rowtime");

        tableEnv.registerTable("sensor", dataTable);

        // 5. 窗口操作
        // 5.1 Group Window
        // table API
        Table resultTable = dataTable.window(Tumble.over("10.seconds").on("rt").as("tw"))
                .groupBy("id, tw")
                .select("id, id.count, temp.avg, tw.end");

        // SQL
        Table resultSqlTable = tableEnv.sqlQuery("select id, count(id) as cnt, avg(temp) as avgTemp, tumble_end(rt, interval '10' second) " +
                "from sensor group by id, tumble(rt, interval '10' second)");

        // 5.2 Over Window
        // table API
        Table overResult = dataTable.window(Over.partitionBy("id").orderBy("rt").preceding("2.rows").as("ow"))
                .select("id, rt, id.count over ow, temp.avg over ow");

        // SQL
        Table overSqlResult = tableEnv.sqlQuery("select id, rt, count(id) over ow, avg(temp) over ow " +
                " from sensor " +
                " window ow as (partition by id order by rt rows between 2 preceding and current row)");

//        dataTable.printSchema();
//        tableEnv.toAppendStream(resultTable, Row.class).print("result");
//        tableEnv.toRetractStream(resultSqlTable, Row.class).print("sql");
        tableEnv.toAppendStream(overResult, Row.class).print("result");
        tableEnv.toRetractStream(overSqlResult, Row.class).print("sql");

        env.execute();
    }
}

測試記錄:


參考:

  1. https://www.bilibili.com/video/BV1qy4y1q728
  2. https://ashiamd.github.io/docsify-notes/#/study/BigData/Flink/%E5%B0%9A%E7%A1%85%E8%B0%B7Flink%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E6%88%98-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0?id=_11-table-api%e5%92%8cflink-sql
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章