阿里妹導讀:7 月 7 日,Flink 1.11.0 正式釋出。歷時近 4 個月,Flink 在生態、易用性、生產可用性、穩定性等方面都進行了增強和改善。Apache Flink PMC、阿里巴巴高階技術專家王治江,同時也是這個版本的 release manager 之一,將和大家一一分享,並深度剖析 Flink 1.11.0 帶來了哪些讓大家期待已久的特性,對一些有代表性的 feature 從不同維度解讀。
在進入深度解讀前,我們先簡單瞭解下社群釋出的一般流程,幫助大家更好的理解和參與 Flink 社群的工作。首先在每個版本的規劃初期,會從志願者中選出 1-2 名作為 release manager。1.11.0 版本我作為中國這邊的 release manager,同時還有一名來自 Ververica 的 Piotr Nowojski 作為德國方的 release manager,這在某種程度上也說明中國的開發者和貢獻度在整個社群的佔比很重要。
接下來會進行這個版本的 feature kickoff。在一些大的方向上,社群的規劃週期可能比較久,會分階段、分步驟跨越多個版本完成,確保質量。每個版本的側重點也會有所不同,比如前兩個版本側重於批處理的加強,而這個版本更側重於流處理易用性的提升。社群規劃的 feature 列表會在郵件列表中發起討論,以收集更多的使用者/開發者意見和反饋。
一般的開發週期為 2-3 個月時間,提前會明確規劃出大概的 feature freeze 時間,之後進行 release candidate 的釋出和測試、以及 bug fix。一般經過幾輪的迭代週期後會正式投票透過一個相對穩定的 candidate 版本,然後基於這個版本正式釋出。
Flink 1.11.0 從 3 月初的功能規劃到 7 月初的正式釋出,歷經了差不多 4 個月的時間,對 Flink 的生態、易用性、生產可用性、穩定性等方面都進行了增強和改善,下面將一一跟大家分享。Flink 1.11.0 從 feature 凍結後釋出了 4 次 candidate 才最終透過。經統計,一共有 236 個貢獻者參與了這次版本開發,解決了 1474 個 jira 問題,涉及 30 多個 FLIP,提交了 2325 個 commit。縱觀近五次版本釋出,可以看出從 1.9.0 開始 Flink 進入了一個快速發展階段,各個維度指標相比之前都有了幾乎翻倍的提高。也是從 1.9.0 開始阿里巴巴內部的 Blink 專案開始被開源 Flink 整合,到 1.10.0 經過兩個大版本已經全部整合完畢,對 Flink 從生態建設、功能性、效能和生產穩定性上都有了大幅的增強。Flink 1.11.0 版本的最初定位是重點解決易用性問題,提升使用者業務的生產使用體驗,整體上不做大的架構調整和功能開發,傾向於快速迭代的小版本開發。但是從上面統計的各個指標來看,所謂的“小版本”在各個維度的資料也絲毫不遜色於前兩個大版本,解決問題的數量和參與的貢獻者人數也在持續增加,其中來自中國的貢獻者比例達到 62%。下面我們會深度剖析 Flink 1.11.0 帶來了哪些讓大家期待已久的特性,從使用者直接使用的 API 層一直到執行引擎層,我們都會選擇一些有代表性的 feature 從不同維度解讀,更完整的 feature 列表請大家關注釋出的 release blog。這兩個維度在某種程度上是相輔相成的,很難嚴格區分開,生態相容上的缺失常常造成使用上的不便,提升易用性的過程往往也是不斷完善相關生態的過程。在這方面使用者感知最明顯的應該就是 Table & SQL API 層面的使用。1 Table & SQL 支援 Change Data Capture(CDC)CDC 被廣泛使用在複製資料、更新快取、微服務間同步資料、審計日誌等場景,很多公司都在使用開源的 CDC 工具,如 MySQL CDC。透過 Flink 支援在 Table & SQL 中接入和解析 CDC 是一個強需求,在過往的很多討論中都被提及過,可以幫助使用者以實時的方式處理 changelog 流,進一步擴充套件 Flink 的應用場景,例如把 MySQL 中的資料同步到 PG 或 ElasticSearch 中,低延時的 temporal join 一個 changelog 等。除了考慮到上面的真實需求,Flink 中定義的“Dynamic Table”概念在流上有兩種模型:append 模式和 update 模式。透過 append 模式把流轉化為“Dynamic Table”在之前的版本中已經支援,因此在 1.11.0 中進一步支援 update 模式也從概念層面完整的實現了“Dynamic Table”。為了支援解析和輸出 changelog,如何在外部系統和 Flink 系統之間編解碼這些更新操作是首要解決的問題。考慮到 source 和 sink 是銜接外部系統的一個橋樑,因此 FLIP-95 在定義全新的 Table source 和 Table sink 介面時解決了這個問題。在公開的 CDC 調研報告中,Debezium 和 Canal 是使用者中最流行使用的 CDC 工具,這兩種工具用來同步 changelog 到其它的系統中,如訊息佇列。據此,FLIP-105 首先支援了 Debezium 和 Canal 這兩種格式,而且 Kafka source 也已經可以支援解析上述格式並輸出更新事件,在後續的版本中會進一步支援 Avro(Debezium) 和 Protobuf(Canal)。CREATE TABLE my_table (
...) WITH (
'connector'='...', -- e.g. 'kafka'
'format'='debezium-json',
'debezium-json.schema-include'='true' -- default: false (Debezium can be configured to include or exclude the message schema)
'debezium-json.ignore-parse-errors'='true' -- default: false
);
2 Table & SQL 支援 JDBC Catalog1.11.0 之前,使用者如果依賴 Flink 的 source/sink 讀寫關係型資料庫或讀取 changelog 時,必須要手動建立對應的 schema。而且當資料庫中的 schema 發生變化時,也需要手動更新對應的 Flink 作業以保持一致和型別匹配,任何不匹配都會造成執行時報錯使作業失敗。使用者經常抱怨這個看似冗餘且繁瑣的流程,體驗極差。實際上對於任何和 Flink 連線的外部系統都可能有類似的上述問題,在 1.11.0 中重點解決了和關係型資料庫對接的這個問題。FLIP-93 提供了 JDBC catalog 的基礎介面以及 Postgres catalog 的實現,這樣方便後續實現與其它型別的關係型資料庫的對接。1.11.0 版本後,使用者使用 Flink SQL 時可以自動獲取表的 schema 而不再需要輸入 DDL。除此之外,任何 schema 不匹配的錯誤都會在編譯階段提前進行檢查報錯,避免了之前執行時報錯造成的作業失敗。這是提升易用性和使用者體驗的一個典型例子。從 1.9.0 版本開始 Flink 從生態角度致力於整合 Hive,目標打造批流一體的 Hive 數倉。經過前兩個版本的迭代,已經達到了 batch 相容且生產可用,在 TPC-DS 10T benchmark 下效能達到 Hive 3.0 的 7 倍以上。1.11.0 在 Hive 生態中重點實現了實時數倉方案,改善了端到端流式 ETL 的使用者體驗,達到了批流一體 Hive 數倉的目標。同時在相容性、效能、易用性方面也進一步進行了加強。在實時數倉的解決方案中,憑藉 Flink 的流式處理優勢做到實時讀寫 Hive:Hive 寫入:FLIP-115 完善擴充套件了 FileSystem connector 的基礎能力和實現,Table/SQL 層的 sink 可以支援各種格式(CSV、Json、Avro、Parquet、ORC),而且支援 Hive table 的所有格式。
Partition 支援:資料匯入 Hive 引入 partition 提交機制來控制可見性,透過sink.partition-commit.trigger 控制 partition 提交的時機,透過 sink.partition-commit.policy.kind 選擇提交策略,支援 SUCCESS 檔案和 metastore 提交。
Hive 讀取:實時化的流式讀取 Hive,透過監控 partition 生成增量讀取新 partition,或者監控資料夾內新檔案生成來增量讀取新檔案。
在 Hive 效能方面,1.10.0 中已經支援了 ORC(Hive 2+)的向量化讀取,1.11.0 中我們補全了所有版本的 Parquet 和 ORC 向量化支援來提升效能。前面也提到過,source 和 sink 是 Flink 對接外部系統的一個橋樑,對於完善生態、可用性及端到端的使用者體驗是很重要的環節。社群早在一年前就已經規劃了 source 端的徹底重構,從 FLIP-27 的 ID 就可以看出是很早的一個 feature。但是由於涉及到很多複雜的內部機制和考慮到各種 source connector 的實現,設計上需要考慮的很全面。從 1.10.0 就開始做 POC 的實現,最終趕上了 1.11.0 版本的釋出。對使用者而言,在 Flink 中改造已有的 source 或者重新實現一個生產級的 source connector 不是一件容易的事情,具體體現在沒有公共的程式碼可以複用,而且需要理解很多 Flink 內部細節以及實現具體的 event time 分配、watermark 產出、idleness 監測、執行緒模型等。
批和流的場景需要實現不同的 source。
partitions/splits/shards 概念在介面中沒有顯式表達,比如 split 的發現邏輯和資料消費都耦合在 source function 的實現中,這樣在實現 Kafka 或 Kinesis 型別的 source 時增加了複雜性。
在 runtime 執行層,checkpoint 鎖被 source function 搶佔會帶來一系列問題,框架很難進行最佳化。
首先在 Job Manager 和 Task Manager 中分別引入兩種不同的元件 Split Enumerator 和 Source reader,解耦 split 發現和對應的消費處理,同時方便隨意組合不同的策略。比如現有的 Kafka connector 中有多種不同的 partition 發現策略和實現耦合在一起,在新的架構下,我們只需要實現一種 source reader,就可以適配多種 split enumerator 的實現來對應不同的 partition 發現策略。
在新架構下實現的 source connector 可以做到批流統一,唯一的小區別是對批場景的有限輸入,split enumerator 會產出固定數量的 split 集合並且每個 split 都是有限資料集;對於流場景的無限輸入,split enumerator 要麼產出無限多的 split 或者 split 自身是無限資料集。
複雜的 timestamp assigner 以及 watermark generator 透明的內建在 source reader 模組內執行,對使用者來說是無感知的。這樣使用者如果想實現新的 source connector,一般不再需要重複實現這部分功能。
目前 Flink 已有的 source connector 會在後續的版本中基於新架構來重新實現,legacy source 也會繼續維護幾個版本保持相容性,使用者也可以按照 release 文件中的說明來嘗試體驗新 source 的開發。眾所周知,Python 語言在機器學習和資料分析領域有著廣泛的使用。Flink 從 1.9.0 版本開始發力相容 Python 生態,Python 和 Flink 合力為 PyFlink,把 Flink 的實時分散式處理能力輸出給 Python 使用者。前兩個版本 PyFlink 已經支援了 Python Table API 和 UDF,在 1.11.0 中擴大對 Python 生態庫 Pandas 的支援以及和 SQL DDL/Client 的整合,同時 Python UDF 效能有了極大的提升。具體來說,之前普通的 Python UDF 每次呼叫只能處理一條資料,而且在 Java 端和 Python 端都需要序列化/反序列化,開銷很大。1.11.0 中 Flink 支援在 Table & SQL 作業中自定義和使用向量化 Python UDF,使用者只需要在 UDF 修飾中額外增加一個引數 udf_type=“pandas” 即可。這樣帶來的好處是:除此之外,1.11.0 中 PyFlink 還支援:PyFlink table 和 Pandas DataFrame 之間無縫切換(FLIP-120),增強 Pandas 生態的易用性和相容性。
Table & SQL 中可以定義和使用 Python UDTF(FLINK-14500),不再必需 Java/Scala UDTF。
Cython 最佳化 Python UDF 的效能(FLIP-121),對比 1.10.0 可以提升 30 倍。
Python UDF 中使用者自定義 metric(FLIP-112),方便監控和除錯 UDF 的執行。
上述解讀的都是側重 API 層面,使用者開發作業可以直接感知到的易用性的提升。下面我們看看執行引擎層在 1.11.0 中都有哪些值得關注的變化。1 支援 application 模式和 Kubernetes 增強1.11.0 版本前,Flink 主要支援如下兩種模式執行:Session 模式:提前啟動一個叢集,所有作業都共享這個叢集的資源執行。優勢是避免每個作業單獨啟動叢集帶來的額外開銷,缺點是隔離性稍差。如果一個作業把某個 Task Manager(TM)容器搞掛,會導致這個容器內的所有作業都跟著重啟。雖然每個作業有自己獨立的 Job Manager(JM)來管理,但是這些 JM 都執行在一個程式中,容易帶來負載上的瓶頸。
Per-job 模式:為了解決 session 模式隔離性差的問題,每個作業根據資源需求啟動獨立的叢集,每個作業的 JM 也是執行在獨立的程式中,負載相對小很多。
以上兩種模式的共同問題是需要在客戶端執行使用者程式碼,編譯生成對應的 Job Graph 提交到叢集執行。在這個過程需要下載相關 jar 包並上傳到叢集,客戶端和網路負載壓力容易成為瓶頸,尤其當一個客戶端被多個使用者共享使用。1.11.0 中引入了 application 模式(FLIP-85)來解決上述問題,按照 application 粒度來啟動一個叢集,屬於這個 application 的所有 job 在這個叢集中執行。核心是 Job Graph 的生成以及作業的提交不在客戶端執行,而是轉移到 JM 端執行,這樣網路下載上傳的負載也會分散到叢集中,不再有上述 client 單點上的瓶頸。使用者可以透過 bin/flink run-application 來使用 application 模式,目前 Yarn 和 Kubernetes(K8s)都已經支援這種模式。Yarn application 會在客戶端將執行作業需要的依賴都透過 Yarn Local Resource 傳遞到 JM。K8s application 允許使用者構建包含使用者 jar 與依賴的映象,同時會根據作業自動建立 TM,並在結束後銷燬整個叢集,相比 session 模式具有更好的隔離性。K8s 不再有嚴格意義上的 per-job 模式,application 模式相當於 per-job 在叢集進行提交作業的實現。除了支援 application 模式,Flink 原生 K8s 在 1.11.0 中還完善了很多基礎的功能特性(FLINK-14460),以達到生產可用性的標準。例如 Node Selector、Label、Annotation、Toleration 等。為了更方便的與 Hadoop 整合,也支援根據環境變數自動掛載 Hadoop 配置的功能。2 Checkpoint & Savepoint 最佳化checkpoint 和 savepoint 機制一直是 Flink 保持先進性的核心競爭力之一,社群在這個領域的改動很謹慎,最近的幾個大版本中幾乎沒有大的功能和架構上的調整。在使用者郵件列表中,我們經常能看到使用者反饋和抱怨的相關問題:比如 checkpoint 長時間做不出來失敗,savepoint 在作業重啟後不可用等等。1.11.0 有選擇的解決了一些這方面的常見問題,提高生產可用性和穩定性。1.11.0 之前, savepoint 中 meta 資料和 state 資料分別儲存在兩個不同的目錄中,這樣如果想遷移 state 目錄很難識別這種對映關係,也可能導致目錄被誤刪除,對於目錄清理也同樣有麻煩。1.11.0 把兩部分資料整合到一個目錄下,這樣方便整體轉移和複用。另外,之前 meta 引用 state 採用的是絕對路徑,這樣 state 目錄遷移後路徑發生變化也不可用,1.11.0 把 state 引用改成了相對路徑解決了這個問題(FLINK-5763),這樣 savepoint 的管理維護、複用更加靈活方便。實際生產環境中,使用者經常遭遇 checkpoint 超時失敗、長時間不能完成帶來的困擾。一旦作業 failover 會造成回放大量的歷史資料,作業長時間沒有進度,端到端的延遲增加。1.11.0 從不同維度對 checkpoint 的最佳化和提速做了改進,目標實現分鐘甚至秒級的輕量型 checkpoint。首先,增加了 Checkpoint Coordinator 通知 task 取消 checkpoint 的機制(FLINK-8871),這樣避免 task 端還在執行已經取消的 checkpoint 而對系統帶來不必要的壓力。同時 task 端放棄已經取消的 checkpoint,可以更快的參與執行 coordinator 新觸發的 checkpoint,某種程度上也可以避免新 checkpoint 再次執行超時而失敗。這個最佳化也對後面預設開啟 local recovery 提供了便利,task 端可以及時清理失效 checkpoint 的資源。其次,在反壓場景下,整個資料鏈路堆積了大量 buffer,導致 checkpoint barrier 排在資料 buffer 後面,不能被 task 及時處理對齊,也就導致了 checkpoint 長時間不能執行。1.11.0 中從兩個維度對這個問題進行解決:1)嘗試減少資料鏈路中的 buffer 總量(FLINK-16428),這樣 checkpoint barrier 可以儘快被處理對齊。這個最佳化有一部分工作已經在 1.11.0 中完成,剩餘部分會在下個版本繼續推進完成。2)實現了全新的 unaligned checkpoint 機制(FLIP-76)從根本上解決了反壓場景下 checkpoint barrier 對齊的問題。實際上這個想法早在 1.10.0 版本之前就開始醞釀設計,由於涉及到很多模組的大改動,實現機制和執行緒模型也很複雜。我們實現了兩種不同方案的原型 POC 進行了測試、效能對比,確定了最終的方案,因此直到 1.11.0 才完成了 MVP 版本,這也是 1.11.0 中執行引擎層唯一的一個重量級 feature。其基本思想可以概括為:
Unaligned checkpoint 在反壓嚴重的場景下可以明顯加速 checkpoint 的完成時間,因為它不再依賴於整體的計算吞吐能力,而和系統的儲存效能更加相關,相當於計算和儲存的解耦。但是它的使用也有一定的侷限性,它會增加整體 state 的大小,對儲存 IO 帶來額外的開銷,因此在 IO 已經是瓶頸的場景下就不太適合使用 unaligned checkpoint 機制。1.11.0 中 unaligned checkpoint 還沒有作為預設模式,需要使用者手動配置來開啟,並且只在 exactly-once 模式下生效。但目前還不支援 savepoint 模式,因為 savepoint 涉及到作業的 rescale 場景,channel state 目前還不支援 state 拆分,在後面的版本會進一步支援,所以 savepoint 目前還是會使用之前的 aligned 模式,在反壓場景下有可能需要很長時間才能完成。Flink 1.11.0 版本的開發過程中,我們看到越來越多來自中國的貢獻者參與到核心功能的開發中,見證了 Flink 在中國的生態發展越來越繁榮,比如來自騰訊公司的貢獻者參與了 K8s、checkpoint 等功能開發,來自位元組跳動公司的貢獻者參與了 Table & SQL 層以及引擎網路層的一些開發。希望更多的公司能夠參與到 Flink 開源社群中,分享在不同領域的經驗,使 Flink 開源技術一直保持先進性,能夠普惠到更多的受眾。經過 1.11.0 “小版本”的短暫調整,Flink 正在醞釀下一個大版本的 feature,相信一定會有很多重量級的特性登場,讓我們拭目以待!