1.5 Apache Flink 基本概念 — 編程模型

Flink 數據流編程模型

1. 抽象級別

Flink 提供了不一樣級別的抽象,以開發流或批處理做業。 html

  • 最底層級的抽象僅僅提供了有狀態流,它將經過過程函數(Process Function)被嵌入到 DataStream API 中。它容許用戶能夠自由地處理來自一個或多個數據流的事件,並使用一致的容錯的狀態,除此以外,用戶能夠註冊事件事件並處理時間回調,從而使程序能夠處理複雜的計算。
  • 實際上,大多數應用並不須要上述的底層抽象,而是針對 核心 API(Core APIs)進行編程,好比 DataStream API(有界或無界流數據)以及 DataSet API(有界數據集)。這些API 爲數據處理提供了通用的構建模塊,好比由用戶定義的多種形式的轉換(transformations),鏈接(joins),聚合(aggregations),窗口操做(windows),狀態(state)等等。這些API處理的數據類型以類(classes)的形式由各自的編程語言所表示。

底層 過程函數(Process Function)與 DataStream API 相集成,使其能夠對某些特定的操做進行底層的抽象。DataSet API 爲有界數據集提供了額外的原語,錄入循環與迭代。redis

  • Table API 是以 表 爲中心的聲明式DSL,其中表可能會動態變化(在表達流數據時)。Table API遵循(擴展的)關係模型:表有二維數據結構(schema)(相似於關係數據庫中的表),同時API提供可比較的操做,例如select、project、join、group-by、aggregate等。Table API程序聲明式地定義了 什麼邏輯操做應該執行 而不是準確地肯定 這些操做代碼的看上去如何 。 儘管Table API能夠經過多種類型的用戶自定義函數(UDF)進行擴展,其仍不如 核心API 更具表達能力,可是使用起來卻更加簡潔(代碼量更少)。除此以外,Table API程序在執行以前會通過內置優化器進行優化。

你能夠在表與 DataStream/DataSet 之間無縫切換,以容許程序將 Table API 與 DataStream 以及 DataSet 混合使用。數據庫

  • Flink提供的最高層級的抽象是 SQL 。這一層抽象在語法與表達能力上與 Table API 相似,可是是以SQL查詢表達式的形式表現程序。SQL抽象與Table API交互密切,同時SQL查詢能夠直接在Table API定義的表上執行。

2. 程序與數據流

Flink程序的基礎構建模塊是 流(streams) 與 轉換(transformations)。(須要注意的是,Flink的DataSet API所使用的DataSets其內部也是流——更多內容將在以後討論。)概念上來說,流是(可能永無止境的)數據記錄流,而 轉換 是一種操做,它取一個或多個流做爲輸入,並生產出一個或多個輸出流做爲結果。apache

執行時,Flink程序映射到 流數據流(streaming dataflows) ,由 流 以及轉換 算符 構成。每個數據流起始於一個或多個 source,並終止於一個或多個 sink。數據流相似於任意的 有向無環圖 (DAG) 。雖然經過 迭代 構造容許特定形式的環,可是大多數狀況下,簡單起見,咱們都不考慮這一點。編程

一般,程序中的轉換與數據流中的操做之間是一對一的關係。有時,然而,一個轉換可能由多個轉換操做構成。windows

3. 並行數據流

Flink程序本質上是並行分佈的。在執行過程當中,一個 流 包含一個或多個 流分區 ,而每個 算符 包含一個或多個 算符子任務 。操做子任務間彼此獨立,以不一樣的線程執行,甚至有可能運行在不一樣的機器或容器上。數據結構

算符子任務的數量即這一特定算符的 並行度 。一個流的並行度即其生產算符的並行度。相同程序中的不一樣的算符可能有不一樣級別的並行度。 編程語言

流在兩個算符之間傳輸數據,能夠經過 一對一 (或稱 forwarding )模式,或者經過 redistributing 模式:函數

  • 一對一 流(例如上圖中 Source 與 map() 算符之間)保持了元素的分區與排序。那意味着 map() 算符的子任務[1]將以與 Source 的子任務[1]生成順序相同的順序查看到相同的元素。
  • Redistributing 流(如上圖中 map() 與 keyBy/window 之間,以及 keyBy/window 與 Sink 之間)則改變了流的分區。每個 算符子任務根據所選擇的轉換,向不一樣的目標子任務發送數據。好比 keyBy() (根據key的哈希值從新分區), broadcast() ,或者 rebalance()(隨機重分區)。在一次 redistributing 交換中,元素間的排序只保留在每對發送與接受子任務中(好比, map() 的子任務[1]與 keyBy/window 的子任務[2])。所以在這個例子中,每一個鍵的順序被保留下來,可是並行確實引入了對於不一樣鍵的聚合結果到達sink的順序的不肯定性。

4. 窗口

聚合事件(好比計數、求和)在流上的工做方式與批處理不一樣。好比,對流中的全部元素進行計數是不可能的,由於一般流是無限的(無界的)。相反,流上的聚合須要由 窗口 來劃定範圍,好比 「計算過去的5分鐘」 ,或者 「最後100個元素的和」 。優化

窗口能夠是 事件驅動的 (好比:每30秒)或者 數據驅動的 (好比:每100個元素)。窗口一般被區分爲不一樣的類型,好比 滾動窗口 (沒有重疊), 滑動窗口 (有重疊),以及 會話窗口 (由不活動的間隙所打斷)

更多窗口的案例能夠查看這個博客

5. 時間

當提到流程序(例如定義窗口)中的時間時,你能夠參考不一樣的時間概念:

  • 事件時間 是事件建立的時間。它一般由事件中的時間戳描述,例如附接在生產傳感器,或者生產服務。Flink經過時間戳分配器訪問事件時間戳。
  • 攝入時間 是事件進入Flink數據流源算符的時間。
  • 處理時間 是每個執行時間操做的算符的本地時間。

更多關於如何處理時間的細節能夠查看事件時間文檔.

6. 有狀態操做

儘管數據流中的不少操做一次只查看一個獨立的事件(好比事件解析器),有些操做卻會記錄多個事件間的信息(好比窗口算符)。 這些操做被稱爲 有狀態的 。

有狀態操做的狀態保存在一個可被視做嵌入式鍵/值存儲的部分中。狀態與由有狀態算符讀取的流一塊兒被嚴格地分區與分佈。所以,只能訪問一個 keyBy() 函數以後的 keyed streams 的鍵/值狀態,而且僅限於與當前事件鍵相關聯的值。對齊流和狀態的鍵確保了全部狀態更新都是本地操做,以在沒有事務開銷的狀況下確保一致性。這種對齊還使得Flink能夠透明地從新分配狀態與調整流的分區。

7. 容錯檢查點

Flink使用 流重放 與 Checkpoint 的結合實現了容錯。Checkpoint與每個輸入流及其相關的每個算符的狀態的特定點相關聯。一個流數據流能夠能夠從一個checkpoint恢復出來,其中經過恢復算符狀態並從檢查點重放事件以保持一致性(一次處理語義)

檢查點間隔是以恢復時間(須要重放的事件數量)來消除執行過程當中容錯的開銷的一種手段。

更多關於checkpoint與容錯的細節能夠查看容錯文檔

8. 流上的批處理

Flink將批處理程序做爲流處理程序的特殊狀況來執行,只是流是有界的(有限個元素)。 DataSet 內部被視爲數據流。上述適用於流處理程序的概念一樣適用於批處理程序,除了一些例外:

  • DataSet API中的程序不使用檢查點。而經過徹底地重放流來恢復。由於輸入是有界的,所以這是可行的。這種方法使得恢復的成本增長,可是因爲避免了檢查點,於是使得正常處理的開銷更小。
  • DataSet API中的有狀態操做使用簡化的im-memory/out-of-core數據結構,而不是鍵/值索引。
  • DataSet API引入了特殊的同步(基於superstep的)迭代,而這種迭代僅僅能在有界流上執行。細節能夠查看迭代文檔
相關文章
相關標籤/搜索