臨時表 Temporal Tables
翻譯自flink官方文檔:https://ci.apache.org/projects/flink/flink-docs-release-1.10/dev/table/streaming/temporal_tables.html#top
臨時表表示一個可變表上(參數化)視圖的概念,該視圖返回表在特定時間點的內容。
可變表可以是跟蹤變更的變更歷史表(如數據庫變更日誌),也可以是具體化變更的可變維度表(如數據庫表)。
對於變更歷史表,Flink可以跟蹤更改,並允許在查詢中以某個時間點爲依據訪問表的內容。在Flink中,這種表由一個臨時表函數(Temporal Table Function)表示。
對於可變維度表,Flink允許在查詢中以處理時間(processing time)爲依據訪問表的內容。在Flink中,這種表由一個臨時表(Temporal Table)表示。
使用臨時表的動機 Motivation
關聯變更記錄表 Correlate with a changing history table
假設有如下,匯率變更歷史記錄表:
SELECT * FROM RatesHistory;
rowtime currency rate
======= ======== ======
09:00 US Dollar 102
09:00 Euro 114
09:00 Yen 1
10:45 Euro 116
11:15 Euro 119
11:49 Pounds 108
RatesHistory表示不斷增長的僅追加的日元匯率的表。
例如,從09:00到10:45的歐元對日元的匯率爲114。從10:45到11:15的匯率爲116。
假設我們要在10:58的時間輸出所有當前匯率,則需要以下SQL查詢來計算結果表:
SELECT *
FROM RatesHistory AS r
WHERE r.rowtime = (
SELECT MAX(rowtime)
FROM RatesHistory AS r2
WHERE r2.currency = r.currency
AND r2.rowtime <= TIME '10:58');
相關子查詢確定對應貨幣的最大時間小於或等於所需時間,外部查詢列出具有最大時間戳的匯率。
下表顯示了這種計算的結果, 在示例中,考慮了10:45時歐元的更新,但是10:58時表的版本中未考慮11:15時歐元的更新和新的英鎊輸入。
rowtime currency rate
======= ======== ======
09:00 US Dollar 102
09:00 Yen 1
10:45 Euro 116
臨時表的概念旨在簡化此類查詢,加快它們的執行速度,並減少Flink的狀態使用。臨時表是僅追加(append-only)表上的參數化視圖,它將append-only表的行解釋爲表的變更日誌,並在特定時間點提供該表的特定版本。將append- only表解釋爲變更日誌需要指定主鍵屬性和時間戳屬性。主鍵確定覆蓋哪些行,時間戳確定行有效的時間。
在上面的示例中,currency將是RatesHistory表的主鍵,rowtime將是timestamp屬性。
在Flink中,這由一個臨時表函數(Temporal Table Function)表示。
關聯可變維度表 Correlate with a changing dimension table
另一方面,一些用例需要連接一個不斷變化的維度表,這個維度表是一個外部數據庫表。
假設LatestRates是一個以最新匯率具體化的表格。 LatestRates是歷史匯率的具體化。 那麼在10:58時的LatestRates表的內容將是:
10:58> SELECT * FROM LatestRates;
currency rate
======== ======
US Dollar 102
Yen 1
Euro 116
在12:00時的LatestRates表的內容將是:
12:00> SELECT * FROM LatestRates;
currency rate
======== ======
US Dollar 102
Yen 1
Euro 119
Pounds 108
在flink中,由臨時表(Temporal Table)表示。
臨時表函數 Temporal Table Function
爲了訪問臨時表中的數據,必須傳遞一個時間屬性,該屬性確定將要返回的表的版本。 Flink使用表函數的SQL語法提供一種表達它的方法。
定義後,臨時表函數將使用單個時間參數timeAttribute並返回一些行。 這些行包含相對於給定時間屬性的所有現有主鍵的行的最新版本。
假設我們基於RatesHistory表定義了一個臨時表函數Rates(timeAttribute),則可以通過以下方式查詢該函數:
SELECT * FROM Rates('10:15');
rowtime currency rate
======= ======== ======
09:00 US Dollar 102
09:00 Euro 114
09:00 Yen 1
SELECT * FROM Rates('11:00');
rowtime currency rate
======= ======== ======
09:00 US Dollar 102
10:45 Euro 116
09:00 Yen 1
對函數Rates(timeAttribute)的每個查詢都將返回給定timeAttribute的Rates狀態。
注意:目前,Flink不支持使用常量時間屬性參數直接查詢臨時表函數。同時,臨時表函數只能在join中使用。 上面的示例僅用於提供有關Rates(timeAttribute)函數返回值的直觀信息。
另請參閱有關用於連續查詢的聯接( joins for continuous queries)的頁面,以獲取有關如何與臨時表聯接的更多信息。
定義臨時表函數 Defining Temporal Table Function
以下代碼段說明了如何從僅追加(append-only)表中創建臨時表函數。
import org.apache.flink.table.functions.TemporalTableFunction;
(...)
// Get the stream and table environments.
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
// Provide a static data set of the rates history table.
List<Tuple2<String, Long>> ratesHistoryData = new ArrayList<>();
ratesHistoryData.add(Tuple2.of("US Dollar", 102L));
ratesHistoryData.add(Tuple2.of("Euro", 114L));
ratesHistoryData.add(Tuple2.of("Yen", 1L));
ratesHistoryData.add(Tuple2.of("Euro", 116L));
ratesHistoryData.add(Tuple2.of("Euro", 119L));
// Create and register an example table using above data set.
// In the real setup, you should replace this with your own table.
DataStream<Tuple2<String, Long>> ratesHistoryStream = env.fromCollection(ratesHistoryData);
Table ratesHistory = tEnv.fromDataStream(ratesHistoryStream, "r_currency, r_rate, r_proctime.proctime");
tEnv.createTemporaryView("RatesHistory", ratesHistory);
// Create and register a temporal table function.
// Define "r_proctime" as the time attribute and "r_currency" as the primary key.
TemporalTableFunction rates = ratesHistory.createTemporalTableFunction("r_proctime", "r_currency"); // <==== (1)
tEnv.registerFunction("Rates", rates); // <==== (2)
第(1)行創建了一個匯率臨時表函數,該函數使我們能夠在Table API中使用函數。
第(2)行在表環境中以Rates名稱註冊了該函數,這使我們可以在SQL中使用Rates函數。
臨時表 Temporal Table
注意:Temporal Table 僅適用於Blink Planner。
爲了訪問臨時表中的數據,當前必須使用LookupableTableSource定義一個TableSource。 Flink使用FOR SYSTEM_TIME AS OF的SQL語法查詢臨時表,該語法於SQL:2011中提出。
假設我們定義了一個名爲LatestRates的臨時表,我們可以通過以下方式查詢此類表:
SELECT * FROM LatestRates FOR SYSTEM_TIME AS OF TIME '10:15';
currency rate
======== ======
US Dollar 102
Euro 114
Yen 1
SELECT * FROM LatestRates FOR SYSTEM_TIME AS OF TIME '11:00';
currency rate
======== ======
US Dollar 102
Euro 116
Yen 1
注意:目前,Flink不支持以固定時間直接查詢臨時表。同時,臨時表只能在聯接中使用。 上面的示例僅用於提供有關臨時表LatestRates返回的直觀信息。
另請參閱有關用於連續查詢的聯接的頁面,以獲取有關如何與臨時表聯接的更多信息。
定義臨時表 Defining Temporal Table
// Get the stream and table environments.
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = TableEnvironment.getTableEnvironment(env);
// Create an HBaseTableSource as a temporal table which implements LookableTableSource
// In the real setup, you should replace this with your own table.
HBaseTableSource rates = new HBaseTableSource(conf, "Rates");
rates.setRowKey("currency", String.class); // currency as the primary key
rates.addColumn("fam1", "rate", Double.class);
// register the temporal table into environment, then we can query it in sql
tEnv.registerTableSource("Rates", rates);
另請參閱有關如何定義LookupableTableSource的頁面。