1. 程式人生 > >(三):Flink資料流程式設計模型

(三):Flink資料流程式設計模型

前言

仍然是學概念,以下大部分是對官方doc的翻譯,但是也會有些個人的理解(主要是對比Spark),以及查詢的一些解決自己的一些疑惑相關資料。

從Flink 的資料流程式設計模型和分散式執行環境的基本概念開始學習會對您瞭解其他部分的文件有幫助,包括安裝以及程式設計指南。強烈推薦先閱讀這兩部分文件。

資料流程式設計模型

抽象級別

Flink提供了不同的抽象級別以支援開發流式、批處理等應用。 在這裡插入圖片描述

  • 最底層級的抽象僅僅提供了有狀態流處理。它將通過過程函式(Process Function)嵌入到DataStream API中。它允許使用者自由地處理來自一個或多個流資料的事件,並使用一致、容錯的狀態。除此之外,使用者可以註冊事件時間和處理事件回撥
    ,從而使程式可實現複雜的計算。
  • 實際上,大多數應用並不需要上面所說的最底層抽象,而是針對 核心API(Core APIs) 進行程式設計,比如DataStream API(有界或無界流資料)以及DataSet API(有界資料集)。這些流暢的API為資料處理提供了通用的構建模組,比如由使用者定義的多種形式的**轉換(transformations),連線(joins),聚合(aggregations),視窗操作(windows),狀態(state)等等。這些API處理的資料型別以類(classes)的形式由各自的程式語言所表示。
  • Table API 是以表為中心的宣告式DSL,其中表可能會動態變化(在表達流資料時)。Table API遵循(擴充套件的)關係模型:表具有附加的模式(類似於關係資料庫中的表),同時API提供可比較的操作,例如select、project、join、group-by、aggregate等。 儘管Table API可以通過多種型別的使用者定義的函式進行擴充套件
    ,其仍不如 核心API 更具表達能力,但是使用起來卻更加簡潔(程式碼量更少)。此外,Table API程式還會通過優化程式,在執行之前應用一些優化規則。【暫時理解為SQL優化?比如Hive中的SQL優化?】【TODO:哪些優化?】 可以在表和DataStream / DataSet之間無縫轉換,允許程式混合Table API以及DataStream和DataSet API。

補充: 與常規SQL語言中將查詢指定為字串不同,Table API查詢是以Java或Scala中的語言嵌入樣式來定義的,具有IDE支援如:自動完成和語法檢測。Elasticsearch有基於json的查詢DSL,不知道是否可以類比理解。

Table API可以用於Scala和Java中,Scala Table API利用了Scala表示式,Java Table API則是基於字串來的,字串會被解析並轉換成等價的表示式。

  • Flink提供的最高階抽象是SQL。這種抽象在語義和表達方面類似於Table API,但是將程式表示為SQL查詢表示式。 SQL抽象與Table API緊密互動,SQL查詢可以在Table API中定義的表上執行。

我對Table API 和 SQL 的理解:

  • 同SQL一樣,這些API操作的物件,也是表。一些情況下,這些通過這些API可以得到與SQL語句相 同的結果,只是Table API呼叫的是函式,比如tableName.groupBy()、select()、where()、filter()、distinct()、join()等,而SQL是連續的語句。所以,Table API更散一些。它們所完成的功能,確實時有重疊的。
  • 要類比的話,Flink中的Table就好比如Spark DataFrame,DataFrame中也可以用groupByKey、union、join這些運算元,而SQL這邊,可以使用SQL語句。

程式與資料流

Flink程式的基礎構建模組是 流(streams) 與 轉換(transformations)。從概念上來講,流是(可能永無止境的)資料記錄流,而轉換是一種操作,它取一個或多個流作為輸入,並生產出一個或多個輸出流作為結果

  • 執行時,Flink程式會被對映到streaming dataflows【整個流程稱之為streaming dataflow】 ,streaming dataflow由流以及轉換運算元構成。
  • 每一個數據流起始於一個或多個 source,並終止於一個或多個 sink
  • 資料流類似於任意的有向無環圖 (DAG) 。雖然通過迭代構造允許特定形式的環,但是大多數情況下,簡單起見,我們都不考慮這一點。

類比Spark: Spark執行程式時,通過寬窄依賴來劃分Stage,然後也是組織為DAG,由TaskScheduler、DAGScheduler執行相應操作。所以,Flink是又怎樣排程作業的? 在這裡插入圖片描述

並行資料流

Flink程式本質上是並行分佈的。在執行過程中,一個 流 包含一個或多個 流分割槽(stream partitions) ,而每一個 operator 包含一個或多個 子operator 任務(operator subtasks) 。operator 子任務間彼此獨立,以不同的執行緒執行,甚至有可能執行在不同的機器或容器上。

operator subtasks的數量即這一特定operator 的 並行度 。【同Spark並行度概念。】

forwarding模式與redistributing模式

流在兩個算符之間傳輸資料,可以通過 一對一 (或稱 forwarding )模式,或者通過 redistributing 模式。

【forwarding的概念類似於Spark中的窄依賴,只是暫時不知道Flink的forwarding模式是否包含多對一;redistributing模式類似於寬依賴,資料可以一對多。】 在這裡插入圖片描述

  • 一對一 流(例如上圖中 Source 與 map() 算符之間)保持了元素的分割槽與順序。意味著 map() 算符的子任務[1]將以與 Source 的子任務[1]生成順序相同的順序檢視到相同的元素。

That means that subtask[1] of the map() operator will see the same elements in the same order as they were produced by subtask[1] of the Source operator.

  • Redistributing 流(如上圖中 map() 與 keyBy/window 之間,以及 keyBy/window 與 Sink 之間)則改變了流的分割槽。每一個 operator 子任務 根據所選擇的轉換,向不同的目標子任務傳送資料。比如 keyBy() (根據key的雜湊值重新分割槽), broadcast() ,或者 rebalance() (隨機重分割槽)。在一次 redistributing 交換中,元素間的排序只保留在每對傳送與接受子任務中(比如, map() 的子任務[1]與 keyBy/window 的子任務[2])。因此在這個例子中,每個鍵的順序被保留下來,但是並行確實引入了不確定性——對於不同鍵的聚合結果到達sink的順序。

but the parallelism does introduce non-determinism regarding the order in which the aggregated results for different keys arrive at the sink.

視窗

流上的聚合事件(比如counts、sums)工作方式與批處理不同。 比如,對流中的所有元素進行計數是不可能的,因為流通常是無限的(無邊界的)。 解決方案是;流上的聚合由視窗來劃定範圍,比如 “count over the last 5 minutes”” ,或者“sum of the last 100 elements” 。

視窗可以是時間驅動的(比如:每30秒)或者資料驅動的 (比如:每100個元素)。 視窗通常被區分為不同的型別,比如 滾動視窗 (沒有重疊), 滑動視窗 (有重疊),以及 會話視窗 (由不活動的間隙所打斷——punctuated by a gap of inactivity)

在這裡插入圖片描述

更多視窗示例可以在此部落格中找到。詳細資訊在視窗文件中。

時間

當在流程式中(例如定義視窗)提到時間時,你可以參考以下不同的時間概念:

  • 事件時間 是一個事件被建立的時間。它通常由事件中的時間戳描述。Flink通過時間戳分配器——timestamp assigners訪問事件時間戳。

  • 攝入時間 是事件進入Flink資料流的source operator的時間。

  • 處理時間 是每一個執行時間操作的算符的本地時間。 在這裡插入圖片描述

關於如何處理時間的更多細節可以檢視文件:事件時間

有狀態操作 Stateful Operations

雖然資料流中的很多操作一次只檢視一個獨立的事件(比如事件解析器),但有些操作卻會記錄多個事件間的資訊(比如視窗算符)。 這些操作被稱為 有狀態的

有狀態操作的狀態儲存在一個可被視作嵌入式鍵/值儲存的部分中。 狀態與stateful operators讀取的流一起,被嚴格地分割槽與分佈——partitioned and distributed。因此,只能在keyBy() 函式之後才能訪問keyed streams 上的鍵/值狀態,並且僅限於與當前事件鍵相關聯的值。 對齊streams中的key、state確保了所有狀態更新都是本地操作,以在沒有事務開銷的情況下確保一致性。這種對齊還使得Flink可以透明地重新分配狀態與調整流的分割槽。 在這裡插入圖片描述 更多資訊請參閱文件:狀態

容錯檢查點

Flink通過使用 流重播——stream replay檢查點 結合的方式實現了容錯。檢查點與每個輸入流中的特定點以及每個操作符的對應狀態相關。 A streaming dataflow can be resumed from a checkpoint while maintaining consistency (exactly-once processing semantics) by restoring the state of the operators and replaying the events from the point of the checkpoint.(一個streaming dataflow可以從一個檢查點恢復出來,其通過恢復operators的狀態並從檢查點重播事件來保持一致性 (恰好一次處理語義))。

流上的批處理

Flink將批處理程式作為流處理程式的特殊情況,只是這種情況下的流是有界的(有限個元素)。 DataSet 內部仍被視為資料流。 上述適用於流處理程式的概念同樣適用於批處理程式,下面是一些區別

  • DataSet API中的程式不使用檢查點。而通過完全地重播流來恢復。因為輸入是有界的,因此這是可行的。這種方法使得恢復的成本增加,但也正是因為避免了檢查點,使得正常情況下的處理的開銷更小。

  • DataSet API中的有狀態操作使用簡化的im-memory/out-of-core資料結構,而不是鍵/值索引

  • DataSet API引入了特殊的同步(基於superstep的)迭代,而這種迭代僅僅能在有界流上執行。細節可以檢視迭代文件

還是不知道流怎樣變為批的,感覺應該是時間視窗,但是上文又說視窗與批處理不同。。。

參考