動機
Flink提供了三種主要的sdk/API來編寫程式:Table API/SQL、DataStream API和DataSet API。我們認為這個API太多了,建議棄用DataSet API,而使用Table API/SQL和DataStream API。當然,這說起來容易做起來難,所以在下面,我們將概述為什麼我們認為太多的api對專案和社群有害。然後,我們將描述如何增強Table API/SQL和DataStream API以包含DataSet API的功能。
在本FLIP中,我們將不描述如何增強Table API/SQL和DataStream的所有技術細節。目標是在棄用DataSet API的想法上達成共識。必須有後續的flip來描述我們所維護的api的必要更改。
為什麼Flink有三個api?
這三種api在專案的生命週期中被有機地開發出來,最初是為特定的用例設計的。DataSet API是Flink最古老的API,支援有界資料的批處理執行。有些人可能不記得了,但Flink最初是一個批處理程式。在早期,社群意識到其基於管道的體系結構非常適合流處理,這就產生了DataStream API。後者是為無界的流用例開發的,具有處理狀態、事件時間和視窗的特殊設施。隨著Flink在分析領域的流行,Table API/SQL被引入,以提供支援批處理和流處理的高階關係API。
對於下面的討論,理解每個API的區別特性將會很有幫助。
DataSet API:
- 只支援有界源
- 沒有為事件時間/視窗提供特殊支援
- 全有或全無輸出:作業要麼產生資料,要麼不產生資料
- 用於大規模批處理作業的細粒度的、基於區域的故障轉移。執行中某一部分的失敗並不一定需要重新啟動整個拓撲
- 高效的資料庫式操作符:雜湊連線、合併連線、對使用輸入資料有界知識的聚合進行排序/分組
DataStream資料API:
- 源可以是有界的,也可以是無界的
- 對事件時間和視窗的特殊支援
- 基於水印或檢查點的“增量”輸出
- 故障恢復檢查點,這意味著在一個運算元失敗的情況下重新啟動整個拓撲
- 你可以執行有界程式,但效率不高:
- 悲觀的假設,沒有結束標識,“你不知道接下來會發生什麼”
- 對於聚合,需要將所有鍵儲存在一個“雜湊對映”中
- 對於事件時間處理,我們需要保持多個“視窗”開啟
- 沒有基於阻塞、持久洗牌的細粒度恢復
Table/SQL API:
- 有界和無界源
- 一個宣告性API,以及SQL
- 資料有一個預先知道的結構,因此允許額外的優化,例如,分組時只反序列化記錄中需要的部分,完全處理二進位制資料,以及整個查詢優化
- 同樣的查詢/程式可用於有界和無界源
- 流和批處理的高效執行,這意味著對於有界執行,我們可以使用DataSet API使用的執行模型,對於流用例,使用DataStrem API的執行模型。這對使用者來說是透明的。
- 沒有低階運算元API,即沒有計時器、狀態
- 不控制生成的執行DAG→查詢優化器會阻止儲存點相容性
自然會出現這樣的問題:“為什麼社群最初不擴充套件DataSet API來處理無界/流式的工作負載,而是新增DataStream API ?”簡單的回答是,我們當時沒有花時間去思考一個API如何同時服務於兩個用例。
為什麼有太多的api不好呢?
我們看到當前局勢存在兩個主要問題:
當需要物理API時,重用Flink應用程式的無界處理和有界處理是不現實的:
我們認為,對於使用者來說,編寫一個管道來分析流資料/無界資料,然後希望重用相同的程式碼來處理有界資料/批處理資料是很常見的。例如,當你想處理S3的歷史資料時,實時管道會從Kafka讀取資料。理論上,可以對有界源使用DataStream API,但無法獲得有效的執行或容錯行為。如果出現故障,整個管道必須重新啟動。這與DataSet API的執行模型不同,在該模型中,只需要重啟單個操作或連線的子圖,因為我們可以保留操作的中間結果。
如果您事先知道所有的輸入,那麼使用事件時間語義就會容易得多。水印可以總是“完美”的,因為沒有早期或晚期資料,我們用於批處理式執行的演算法和資料結構可以考慮到這一點。
DataSet和DataStream API有不同的可用聯結器集,因為它們使用不同的API來定義源和接收。例如,你不能用批處理型別的作業從Kafka讀取一個有界的區間。
最後,我們認為DataStream API的事件時間/視窗特性對於批處理也很有用。例如,當您想要處理時間序列資料時。目前,您可以使用DataStream API並處理低於標準的執行行為,或者使用DataSet API並使用排序/分組手動實現視窗。
使用者必須提前在api之間做出選擇:
這增加了認知負荷,使Flink對新使用者來說更不容易接近。如果他們一開始就做出了錯誤的選擇,那麼如果沒有大量的時間投資,他們將無法在以後轉換。
另一個方面是,想要採用Flink的大型組織可能會因為不得不對工程師進行兩種不同api及其潛在語義差異的培訓而受挫,例如什麼是延遲,什麼是事件時間,以及它是否與批處理相關等。
修改建議
我們建議棄用DataSet API,而使用Table API/SQL和DataStream API。為了實現這一點,我們需要增強Table API/SQL和DataStream API,使其成為以前使用DataSet API的情況下可用的替代品。我們將在這裡概述所需的更改,但將更具體的計劃推遲到後續的FLIPs。對於這個提議,我們只是想讓社群就棄用DataSet API的總體想法達成共識,並概述其他API所需的變化。
對“倖存者”api的更改
Table/SQL API:
- 必須容易在程式碼中內聯定義源/接收器。這在FLIP-129:重構描述符API中進行了介紹,以在表API中註冊聯結器。
- 我們應該在Table上有易於使用的“命令式”操作,也就是應該有“人體工程學”map/filter/flatMap。這些操作應該是面向行/記錄的,而不是常規Table API操作的面向列的性質。這樣使用者就不必學習表示式DSL語法來編寫操作。
- 此外,我們還希望棄用/刪除遺留表API批處理計劃器以及批執行環境,因為它們與DataSet API互操作
DataStream資料API:
- 我們需要一個適用於有界和無界源的源API。這在FLIP-27:重構源介面中有涉及。
- 我們需要一個適用於無界和有界源的接收器API。這是由FLIP-143:統一匯聚API覆蓋的
- 我們需要定義一組通用的執行語義,用於批處理和流執行,這包括重新考慮DataStream API中的一些決策,使它們在一個統一的世界中工作。這在FLIP-134: DataStream API的批處理執行中得到了介紹
- 當拓撲有界時,我們需要為DataStream程式使用高效的批處理式執行。這在FLIP-140中有涉及:為有界鍵控流引入批處理樣式的執行
- 特別是對於機器學習用例,我們需要對迭代計算的健壯支援。目前DataStream API中對迭代的支援應該被認為是實驗性的,它不像DataSet API那樣支援動態終止標準。然而,我認為我們需要在後續FLIP中解決這個問題。
哪些用例應該使用什麼API/SDK
我們目前還沒有明確的指南來指導使用者應該使用哪種API。我們需要就建議達成一致,然後在檔案和一般營銷中積極推廣它們。這可能會以決策樹或其他圖形化決策工具/應用的形式出現。
我們目前的想法總結如下:
- 如果你有一個模式,沒有“低階”操作→SQL/Table
- 如果你需要顯式控制執行圖、操作、操作中的狀態→使用DataStream API
也就是說,應該可以在Table API和DataStream API之間自由轉換。FLIP-136:改善DataStream和Table API之間的互操作性使這些工作更加具體。
相容性、棄用和遷移計劃
DataSet API應該在文件和程式碼中標記為已棄用,並描述專案的未來方向。在即將到來的版本中。
- 使用者有足夠的時間將現有的用例遷移到其他API,
- 我們確信剩餘的API足以覆蓋DataSet API的用例,我們就應該刪除DataSet API。
這取決於上面提到的後續FLIPs,所以當滿足上面概述的條件時,這個FLIPs可以被認為是完整的。
重要的是要記住,我們不能簡單地將DataSet API從一個版本移到下一個版本。這將是一個較長的過程,我們需要確保DataSet API的現有使用者能夠遷移並進行遷移。有些公司在DataSet API上投入了大量資金,但他們不能把它們拋在後面。
拒絕選擇
我們認為,如果可用api的重疊像DataStream和DataSet api那樣明顯,那麼除了減少可用api的數量別無選擇。理論上我們可以棄用DataStream API,支援DataSet API,但我們認為DataStream API是更廣泛使用的API,而且目前它的功能也更齊全(請參閱事件時間處理和視窗)。這也與“批處理是流的一部分,批處理是流處理的嚴格子集”的思想產生了很好的共鳴。
關注gzh HEY DATA 一起交流更多。