Flink不止於計算,存算一體才是未來

伴隨着實時化浪潮的發展和深化,Flink 已逐步演進為實時流處理的領軍技術和事實標準。Flink 一方面持續優化其流計算核心能力,不斷提高整個行業的流計算處理標準,另一方面沿着流批一體的思路逐步推進架構改造和應用場景落地,但是,隨着計算流批統一的逐漸完善的同時,Flink存儲的流批統一缺陷顯得尤為捉襟見肘

Flink 這幾年一直在反覆強調流批一體,即:使用同一套 API、同一套開發範式來實現大數據的流計算和批計算,進而保證處理過程與結果的一致性。

但是,之前Flink一直強調的僅僅是計算層的流批一體,至於流批一體,還有哪些層面呢?

  • 數據集成流批一體:離線與實時是否使用統一數據採集方式;如統一通過 CDC 或者 OGG 將數據實時捕獲推送到 kafka,批與流在從 kafka 中消費數據,載入明細層。

  • 數據存儲流批一體:離線與實時數據是否統一分層、統一存儲;兼容數據的一致性和實時性。

  • 處理邏輯流批一體:流與批處理是否使用統一 SQL 語法或者 ETL 組件,再通過底層分別適配流與批計算引擎,保證數據口徑的一致性。

  • 計算引擎流批一體:流與批使用同一套計算引擎,從根本上避免同一個處理邏輯流批兩套代碼問題。

其實,在解決了計算層的問題之後,掣肘的便是數據存儲。目前,很多實時數倉中,實時鏈路採用kafka之類的消息隊列,但是中間消息隊列數據不利於分析。如果用戶想要分析實時鏈路中一個明細層的數據,其實非常不方便,很多用戶目前採用的辦法可能是先把這個明細層中的數據導出來,比如導到 Hive 做離線分析,但這個時效性會大幅下降,或者為了加速查詢,把數據導入到其他 OLAP 引擎中,但這又會增加系統複雜度,且數據一致性同樣很難保證。

截止到目前,整個行業還沒有完整的一站式解決計算引擎和數據存儲流批一體的技術方案,這對當前流式計算引擎提出了更高的要求和挑戰,不過慶幸的是,flink已經在這方面布局,在下一個迭代版本flink1.5中,被定義為流批一體的數據存儲系統的Flink Dynamic Table即將面世。

毫無疑問,這對整個行業是巨大的創新。

提到flink發展存儲系統,我們不得不先回顧傳統大數據架構的演化過程,以史為鏡,才能發現存儲計算的一體的重要性和緊迫性

一 迭代中的數據倉庫,磕磕絆絆

實時數倉的架構,從經典的主題建模,到維度建模,再到hadoop體系,後面的lamda架構,kappa架構,在逐步完善,但一直沒有形成完整的解決方案。

1)離線數倉

使用hadoop平台的hive做數據倉庫,報表層數據保存在mysql中,使用tableau做報表系統,這樣不用擔心存儲問題、計算速度也大大加快了。在此基礎上,提供hue給各個部門使用,這樣簡單的取數工作可以由運營自己來操作,使用presto可以做mysql、hive的跨庫查詢,大大提升了查詢效率。

2)Lambda架構

為了計算一些實時指標,就在原來離線數倉的基礎上增加了一個實時計算的鏈路,並對數據源做流式改造(即把數據發送到消息隊列),實時計算去訂閱消息隊列,直接完成指標增量的計算,推送到下游的數據服務中去,由數據服務層完成離線&實時結果的合併

需要注意的是流處理計算的指標批處理依然計算,最終以批處理為準,即每次批處理計算後會覆蓋流處理的結果(這僅僅是流處理引擎不完善做的折中)。

Lambda架構整合離線計算和實時計算,融合不可變性(Immunability),讀寫分離和複雜性隔離等一系列架構原則,可集成Hadoop,Kafka,Storm,Spark,Hbase等各類大數據組件。

同樣的需求需要開發兩套一樣的代碼,這是Lambda架構最大的問題,兩套代碼不僅僅意味着開發困難(同樣的需求,一個在批處理引擎上實現,一個在流處理引擎上實現,還要分別構造數據測試保證兩者結果一致),後期維護更加困難,比如需求變更後需要分別更改兩套代碼,獨立測試結果,且兩個作業需要同步上線。

資源佔用增多:同樣的邏輯計算兩次,整體資源佔用會增多(多出實時計算這部分)。

實時鏈路和離線鏈路計算結果容易讓人誤解,昨天看到的數據和今天看到的數據不一致**。

下游處理複雜,需要整合實時和離線處理結果,這一部分往往是我們在呈現給用戶之前就完成了的。

3)Kappa架構

 

再後來,實時的業務越來越多,事件化的數據源也越來越多,實時處理從次要部分變成了主要部分,架構也做了相應調整,出現了以實時事件處理為核心的Kappa架構。當然這不要實現這一變化,還需要技術本身的革新——Flink,Flink 的出現使得Exactly-Once 和狀態計算成為可能,這個時候實時計算的結果保證最終結果的準確性。

Lambda架構雖然滿足了實時的需求,但帶來了更多的開發與運維工作,其架構背景是流處理引擎還不完善,流處理的結果只作為臨時的、近似的值提供參考。後來隨着Flink等流處理引擎的出現,流處理技術很成熟了,這時為了解決兩套代碼的問題,LickedIn 的Jay Kreps提出了Kappa架構。

Kappa架構可以認為是Lambda架構的簡化版(只要移除lambda架構中的批處理部分即可)。在Kappa架構中,需求修改或歷史數據重新處理都通過上游重放完成。

存在的問題:Kappa架構最大的問題是流式重新處理歷史的吞吐能力會低於批處理,但這個可以通過增加計算資源來彌補。

4)混合架構

在真實的場景中,很多時候並不是完全規範的Lambda架構或Kappa架構,可以是兩者的混合,比如大部分實時指標使用Kappa架構完成計算,少量關鍵指標(比如金額相關)使用Lambda架構用批處理重新計算,增加一次校對過程

Kappa架構並不是中間結果完全不落地,現在很多大數據系統都需要支持機器學習(離線訓練),所以實時中間結果需要落地對應的存儲引擎供機器學習使用,另外有時候還需要對明細數據查詢,這種場景也需要把實時明細層寫出到對應的引擎中。

還有就是Kappa這種以實時為主的架構設計,除了增加了計算難度,對資源提出了更改的要求之外,還增加了開發的難度,所以才有了下面的混合架構,可以看出這個架構的出現,完全是出於需求和現狀考慮的。

混合架構在解決了部分業務問題的同時,也帶了架構的複雜性,在計算引擎及存儲介質上,存在多元性,那麼不管是學習成本還是開發成本以及後期的維護成本,都是指數級的增長,未必是一種最優的選擇。

同樣,混合架構支持實時入湖、入湖實時增量分析,但這些場景的實時性大打折扣,因為數據湖存儲格式本質還是 Mini-Batch,實時計算在混合架構中退化到 Mini-Batch 模式。毫無疑問,這對實時性要求很高的業務是很大的災難。

二 Flink流批一體的架構演進,帶來無限想像

Flink 流批一體在技術架構演進和落地應用兩方面都有了新進展。

技術演進層面,Flink 流批一體 API 和架構改造已經完成,在原先的流批一體 SQL 基礎上,進一步整合了 DataStream 和 DataSet 兩套 API,實現了完整的 Java 語義層面的流批一體 API,架構上做到了一套代碼可同時承接流存儲與批存儲。

在2021年 10 月發佈的 Flink 1.14 版本中,已經可以支持在同一個應用中混合使用有界流和無界流:Flink 現在支持對部分運行、部分結束的應用(部分算子已處理到有界輸入數據流的末端)做 Checkpoint。此外,Flink 在處理到有界數據流末端時會觸發最終 Checkpoint,以確保所有計算結果順利提交到 Sink。

而批執行模式現在支持在同一應用中混合使用 DataStream API 和 SQL/Table API(此前僅支持單獨使用 DataStream API 或 SQL/Table API)。

此外,Flink 更新了統一的 Source 和 Sink API,開始圍繞統一的 API 整合連接器生態。新增的混合 Source 可在多個存儲系統間過渡,實現諸如先從 Amazon S3 中讀取舊的數據再無縫切換到 Apache Kafka 這樣的操作。

三 Flink CDC ,流批一體走向成熟的助推器

數據集成、不同數據源之間的數據同步對於很多團隊來說是剛需,但傳統方案往往複雜度太高且時效性不好。傳統的數據集成方案通常是離線數據集成和實時數據集成分別採用兩套技術棧,其中涉及很多數據同步工具,比如 Sqoop、DataX 等,這些工具要麼只能做全量要麼只能做增量,開發者需要自己控制全增量的切換,配合起來比較複雜。

這個時候,flink cdc粉墨登場,對變更數據實時捕獲。基於 Flink 的流批一體能力和 Flink CDC,只需要寫一條 SQL,就可以做到先全量同步歷史數據,再自動斷點續傳增量數據,實現一站式數據集成。全程無需用戶判斷和干預,Flink 能自動完成批流之間的切換並保證數據的一致性。

Flink CDC Connectors 作為一個獨立的開源項目,從去年 7 月份開源以來,一直保持相當高速的發展,平均兩個月一個版本。目前 Flink CDC 版本已經更新到 2.1 版本,並完成了很多主流數據庫的適配,比如 MySQL、PostgreSQL、MongoDB、Oracle 等,更多數據庫如 TiDB、DB2 等的對接工作也在進行中。可以看到已經有越來越多企業在自己的業務場景中使用 Flink CDC。

四 Flink流式數倉,讓存算一體的不再是遠方

Flink 流批一體的理念可以在上述場景下得到充分應用。Flink 可以讓當前業界主流數倉架構再進階一層,實現真正端到端全鏈路的實時化分析能力,即:當數據在源頭髮生變化時就能捕捉到這一變化,並支持對它做逐層分析,讓所有數據實時流動起來,並且對所有流動中的數據都可以實時查詢。再藉助 Flink 完備的流批一體能力,使用同一套 API 就可以同時支持靈活的離線分析。這樣一來,實時、離線以及交互式查詢分析、短查詢分析等,就可以統一成一整套解決方案,成為理想中的「流式數倉(Streaming Warehouse)」。

流式數倉(Streaming Warehouse)更準確地說,其實是「make data warehouse streaming」,就是讓整個數倉的數據全實時地流動起來,且是以純流的方式而不是微批(mini-batch)的方式流動。目標是實現一個具備端到端實時性的純流服務(Streaming Service),用一套 API 分析所有流動中的數據,當源頭數據發生變化,比如捕捉到在線服務的 Log 或數據庫的 Binlog 以後,就按照提前定義好的 Query 邏輯或數據處理邏輯,對數據進行分析,分析後的數據落到數倉的某一個分層,再從第一個分層向下一個分層流動,然後數倉所有分層會全部流動起來,最終流到一個在線系統里,用戶可以看到整個數倉的全實時流動效果。

在這個過程中,數據是主動的,而查詢是被動的,分析由數據的變化來驅動。同時在垂直方向上,對每一個數據明細層,用戶都可以執行 Query 進行主動查詢,並且能實時獲得查詢結果。此外,它還能兼容離線分析場景,API 依然是同一套,實現真正的一體化。

目前業界還沒有這樣一個端到端全流式鏈路的成熟解決方案,雖然有純流的方案和純交互式查詢的方案,但需要用戶自己把兩套方案加起來,必然會增加系統的複雜性,如果要再把離線數倉方案也加進來,系統複雜性問題就更大了。流式數倉要做的是在實現高時效性的同時,不進一步提高系統複雜性,讓整個架構對於開發和運維人員來說都是非常簡潔的。

當然,流式數倉是終態,要達成這個目標,Flink 需要一個配套的流批一體存儲支持。其實 Flink 本身有內置的分佈式 RocksDB 作為 State 存儲,但這個存儲只能解決任務內部流數據狀態的存儲問題。

流式數倉需要一個計算任務之間的表存儲服務:第一個任務將數據寫進去,第二個任務就能從它實時地再讀出來,第三個任務還能對它執行用戶的 Query 分析。因此 Flink 需要再擴展出一個跟自身理念配套的存儲,從 State 存儲走出來,繼續向外走。為此,Flink 社區提出了新的 Dynamic Table Storage,即具備流表二象性的存儲方案。

Flink Dynamic Table可以理解為一套流批一體的存儲,並無縫對接 Flink SQL。原來 Flink 只能讀寫像 Kafka、HBase 這樣的外部表,現在用同一套 Flink SQL 語法就可以像原來創建源表和目標表一樣,創建一個 Dynamic Table。

流式數倉的分層數據可以全部放到 Flink Dynamic Table 中,通過 Flink SQL 就能實時地串聯起整個數倉的分層,既可以對 Dynamic Table 中不同明細層的數據做實時查詢和分析,也可以對不同分層做批量 ETL 處理

從數據結構上看,Dynamic Table 內部有兩個核心存儲組件,分別是 File Store 和 Log Store。顧名思義,Flie Store 存儲 Table 的文件存儲形式,採用經典的 LSM 架構,支持流式的更新、刪除、增加等;同時,採用開放的列存結構,支持壓縮等優化;它對應 Flink SQL 的批模式,支持全量批式讀取。而 Log Store 存儲的是 Table 的操作記錄,是一個不可變更序列,對應 Flink SQL 的流模式,可以通過 Flink SQL 訂閱 Dynamic Table 的增量變化做實時分析,目前支持插件化實現。

未來,利用 Flink CDC、Flink SQL、Flink Dynamic Table 就可以構建一套完整的流式數倉,實現實時離線一體化及對應計算存儲一體化的體驗。那便是大數據技術,flink技術發展的又一個精進高度。

Tags: