流概念
對于流和批處理程序,Table API和SQL是統(tǒng)一支持的。這就意味著Table API和SQL的查詢有相同的語義,不管輸入是一個有界的批輸入還是無界的流輸入。因為關系代數和SQL最初是為批處理程序設計的,無界流輸入的關系查詢不如有界批處理的關系查詢好理解。
在這個頁面,我們講解概念,實踐的局限性和流數據關系API的具體配置參數。
流數據的關系查詢
SQL和關系代數還沒有考慮流式數據的設計。因此,關系代數(SQL)和流處理在概念上的差距很小。
關系代數/SQL
1.關系和表示有界的多元組集合
2.在批處理數據上執(zhí)行的一個查詢可以訪問完整的輸入數據(例如:關系數據庫)
3.批處理查詢在生成一個固定大小的結果后終止
流處理
1.流是一個無界序列的元組
2.流查詢在啟動時不能訪問所有的數據,必須等待數據流進來。
3.流查詢根據其接收到的數據不斷的更新結果,從不完成。
盡管有這么多差異,處理流的關系查詢和SQL也不是不可能的。高級的關系數據庫系統(tǒng)提供了一個物化視圖的特性。物化視圖被定義為SQL查詢,就像一個常規(guī)的虛擬視圖,與虛擬視圖相反,物化視圖緩存查詢結果,這樣在訪問視圖時,不需要對查詢進行評估。緩存最常見的挑戰(zhàn)是緩存過時的數據。當定義查詢的基本表被修改時,一個物化視圖就過期了。Eager View Maintenance是一種更新物化視圖并在其基本表更新后更新物化視圖的技術。
如果我們考慮下面的問題,流視圖和SQL查詢之間的連接就變得很明顯了:
1.一個數據庫表是一個流的插入、更新和刪除DML語句的結果,通常被稱為更新流
2.物化視圖被定義為SQL查詢,為了更新視圖,查詢是連續(xù)不斷的處理視圖基本關系流的更新日志
3.流式SQL的查詢結果是物化視圖。
動態(tài)表和連續(xù)查詢
動態(tài)表示Table API和SQL支持流數據的核心概念。與表示批處理的靜態(tài)表先相比,動態(tài)表隨著時間變化更新。可以像靜態(tài)批處理那樣查詢它們。查詢一個動態(tài)表會產生連續(xù)查詢。連續(xù)查詢從不終止,并且生成一個動態(tài)表作為結果。查詢不斷的更新結果表反映輸入表的變化。基本上,動態(tài)表的連續(xù)查詢非常類似于物化視圖的定義的查詢。
需要注意的是連續(xù)查詢的結果總是在語義上等同于在輸入表上以批處理模式執(zhí)行的查詢結果
下圖顯示了流、動態(tài)表和連續(xù)查詢的關系
流----->動態(tài)表---->連續(xù)查詢---->動態(tài)表---->流
1.流可以被轉化為一個動態(tài)表
2.動態(tài)表上進行連續(xù)查詢會產生一個新的動態(tài)表
3.結果動態(tài)表可以被轉換回成一個流
注意:動態(tài)表是最重要的一個邏輯概念。在執(zhí)行查詢的過程中,動態(tài)表不一定完全實現。
下面我們用一個含有以下sehema的單擊事件流講解動態(tài)表和連續(xù)查詢的概念:
[
user:? VARCHAR,? // the name of the user
cTime: TIMESTAMP, // the time when the URL was accessed
url:? VARCHAR? ? // the URL that was accessed by the user
]
定義流上的表
為了用關系查詢處理流,它必須被轉化成一張表。從概念上講,流的每一條記錄被解釋為對結果表的插入修改。基本上,我們從流的插入變更記錄構建一個表。
下圖顯示了一個單擊事件流是如何轉換成一個表的。隨著流更多的記錄被插入,結果表不斷的增長。
注意:在留上定義的表在內部不是物化的。
連續(xù)查詢
連續(xù)查詢被計算于一個動態(tài)表上,并產生一個新的動態(tài)表作為結果。與批處理查詢相反,一個連續(xù)查詢從不中斷,并且根據輸入表的更新來更新它的結果表。在任何時間點,一個連續(xù)查詢的結果在語義上等價于在輸入表上以批處理模式執(zhí)行的查詢結果。
下面我們展示了兩個例子查詢一個單擊事件流上定義的單擊表。
第一個查詢是一個簡單的分組、統(tǒng)計聚合查詢。根據user字段將單擊表分組并統(tǒng)計URL的訪問次數。下面的圖展示了單擊表隨著時間的推移額外的增加行是如何查詢計算的。

當查詢啟動時,這個單擊表(左手邊)是空的,當第一條數據被插入到單擊表中,查詢開始計算結果表,第一條數據 [Mary, ./home] 被插入后,結果表(右手邊,頂部那個)由[Mary, 1]一行組成。當第二條數據[Bob, ./cart]被插入單擊表后,查下更新結果表并插入了新的一行[Bob, 1]。第三行[Mary, ./prod?id=1] 產生一個已經計算好結果行的更新,由[Mary, 1] 更新為 [Mary, 2]。最后,當第第四條數據被追加到單擊表中后,查詢會往結果表插入第三條數據[Liz, 1]。
第二個查詢類似于第一個查詢,不過單擊表除了按用戶屬性分組外還有一個按小時滾動的窗口,在統(tǒng)計URL數量之前(基于時間的計算就像基于一個特殊時間屬性的窗口)。再一次,下圖顯示了不同時間點的輸入和輸出,可視化展示的動態(tài)表的變化。

如之前,左邊顯示的單擊表輸入。查詢每小時連續(xù)不斷的計算結果,并更新結果表。單擊表根據時間戳從12:00:00 到 12:59:59 之間包含4條數據。查詢根據輸入(每個用戶一個)計算出兩行結果并追加到結果表。在下一個13:00:00到13:59:59窗口,單擊表包含3行數據,另外的兩行結果被追加到結果表,隨著時間的推移,更多的數據被追加到單擊表,導致結果表被更新。
查詢的更新和追加
盡管這兩個查詢例子看起來比較類似(二者都是分組聚合統(tǒng)計),他們在一個重要的方面有所不同,第一個查詢更新以前發(fā)射結果,更新流定義的結果表包含 INSERT 和 UPDATE 變更。第二個查詢僅僅追加結果表,更新流定義的結果表緊由INSERT組成。
無論查詢只生成一個僅追加的表或者一個更新的表有一些含義,產生更新變更的查詢通常要保持更多的狀態(tài)(見下面的章節(jié))。追加表轉化為流不同于更新表(見 表轉換為流 章節(jié))。
查詢限制
很多,但也不是全部,在流上,可以對語義有效的查詢進行計算作為一個連續(xù)查詢,有些的查詢的計算開銷太大,要么由于他們要需要維護state的大小,要么計算更新太貴了。
State大小
一個無界流上的連續(xù)查詢被計算通常會通常要跑幾個星期或幾個月,因此,連續(xù)查詢的數據總量可能會非常大。必須更新以前發(fā)出結果的查詢需要維護所有發(fā)的行以便能夠更新他們。例如,第一個查詢需要存儲每一個用戶的URL統(tǒng)計以便能夠增加統(tǒng)計并且發(fā)出一個新的結果,當輸入表接收大一條新的數據,如果只跟蹤注冊用戶,保持的數量可能不會太高。然而,一個非注冊的用戶獲得一個唯一的用戶名,要維護的的數可能會隨著時間的增長而增長,最終導致查詢失敗。
SELECT user,COUNT(url) FROM clicks GROUP BY user;
計算更新
一些查詢需要重新計算并更新發(fā)射結果行的很大一部分,即使只有單個的一條記錄被添加或更新,顯而易見,這種查詢不太適合作為連續(xù)查詢執(zhí)行,下面的查詢例子是根據最后一次單擊的時間計算每一個用戶的等級。只要單擊表接收一條新的記錄,用戶的最后一個動作被更新且一個新的等級被計算,然而由于兩行不能有相同的等級,所有的低等級的行也需要被更新。
SELECT user,RANK()OVER(ORDERBYlastLogin)FROM(SELECTuser,MAX(cTime)ASlastActionFROMclicksGROUPBYuser);
QueryConfig這個章節(jié)討論了控制執(zhí)行連續(xù)查詢的參數。有些參數可以用來維護狀態(tài)的大小用于結果的準確性
表轉換為流
一個動態(tài)表可以被INSERT、UPDATE和DELETE連續(xù)的修改就像一個普通的數據庫表。他可能是一個單行的表,不斷的更新,一個只插入的表,沒有UPDATE和DELETE修改,或者兩者之間的任何東西。
當將動態(tài)表轉換為流或者寫入其他外部系統(tǒng)時,這些改變需要編碼。Flink的 Table API&SQL支持3中辦法編碼改變一個動態(tài)表
只追加的流
一個只被 INSERT 變更修改的動態(tài)表可以通過插入行來轉換成流
回縮流
回縮流是具有兩種消息類型的流,添加消息和撤回消息,一個動態(tài)表被轉換成回縮流,通過編寫一個插入變更作為添加消息,一個刪除變更作為撤回消息,和一個更新變更作為撤回消息,更新前的作為撤回消息,更新后的新行作為添加消息。下圖示意一個動態(tài)表轉為一個回縮流。

更新插入流(upsert)
一個更新插入流是有兩個類型的消息流,更新插入消息和刪除消息,一個動態(tài)表轉換成一個更新插入流需要一個唯一的Key(可能復合),具有唯一鍵的動態(tài)表被轉換成動態(tài)表是通過編寫INSERT和UPDATE變更作為更新插入消息,刪除變更作為刪除消息。流的消耗操作符需要知道唯一的Key屬性,以便能夠正確的應用消息。與回溯流最主要的不同是編寫更新變更是一個單獨的消息,也因此更高效。下圖示意了一個動態(tài)表轉換成更新插入流

把動態(tài)表轉換為DataStream的API在通用概念這個頁面討論。請注意當轉換動態(tài)表為DataStream是指支持追加流和回縮流。TableSink接口發(fā)射一個動態(tài)表到一個外部系統(tǒng)在TableSources and TableSinks頁面討論。
時間屬性
Flink能夠基于不同的時間點處理流數據。
Processing time 指的是各自執(zhí)行操作的機器的系統(tǒng)時間。
Event time 指的是基于每一行的時間戳處于流數據。時間戳可以編寫為事件發(fā)生的時間。
Ingestion time 是事件進入Flink的時間,本質上和Event time類似
關于更多的Flink的時間的處理,詳見Event Time and Watermarks.介紹
表程序需要為流式環(huán)境指定響應的時間特性。
Table API 和SQL 二者基于時間的操作如同窗口需要時間的概念信息和它的起點。因此。表可以提供邏輯時間屬性為指示時間及訪問相應的時間戳在表程序中。