摘要:本文整理自米哈遊大資料實時計算團隊負責人張劍,在 FFA 行業案例專場的分享。本篇內容主要分為三個部分:
- 發展歷程
- 平臺建設
- 未來展望
一、發展歷程
隨著公司業務的發展,實時計算需求應運而生。我們根據重點的工作內容將發展階段劃分為三個部分,
- 第一階段是以 DataStream API 開發為主的 Flink 平臺
- 第二個階段是以 Flink SQL 為主一站式開發平臺
- 第三階段是一站式開發平臺的功能深化和場景覆蓋
第一階段,以 DataStream API 開發為主的 Flink 平臺,很好的解決了我們對於實時計算的需求。但隨著開發同學越來越多,大家發現基於 DataStream API 開發為主的實時計算平臺,具有三個弊端,分別是開發成本高、版本易衝突、運維難度大,因此大家對 Flink SQL 的呼聲就越來越高。
第二階段,以 Flink SQL 為主一站式開發平臺。主要的工作內容有:Flink SQL 能力提升、指標和日誌體系建設、後設資料和血緣管理。基於此,業務人員有了新的期望。
- 第一,希望平臺能夠更加智慧化,降低使用者的使用調參、調優等成本
- 第二,希望流量的波動能夠具有自動擴縮容的資源管理能力
- 第三,希望資料更具時效性。比如資料入倉、入湖後分鍾級可查,或者基於近實時數倉開發
第三階段,一站式開發平臺功能深化和場景覆蓋。主要的工作和未來要持續做的工作包含如下幾個方面:
- 第一,任務資源的靜態和動態調優能力
- 第二,資源的彈性擴縮容能力
- 第三,加強近實時數倉的建設
下面我們進入平臺的整體架構,從下圖中可以看到平臺總體包含三個部分,分別是使用者許可權及鑑權、功能和服務模組、以及環境和資源功能。
功能和服務主要包含作業大盤、概覽、開發、運維、日誌、後設資料、血緣、監控告警、資源調優、自動擴縮容、彈性資源管理以及安全管控等。
二、平臺建設
那麼基於這樣的實時計算平臺,我們是如何建設的呢?圍繞 Flink SQL 或者平臺化的主要工作有如下四個方面:
- 第一,語義表達和控制能力的建設
- 第二,資源調優和彈效能力的建設
- 第三,指標體系建設
- 第四,近實時數倉建設
截止目前,Flink SQL 佔比總任務數已經在 90%以上,極大的提高了大家的開發效率。下面我們將對每一個部分進行詳細的講解,來看一看具體都是怎麼做的。
DataStream API 相較於 Flink SQL 有如下幾個優點:
- 第一,運算元並行度和傳輸方式可控
- 第二,執行圖直觀易於理解
- 第三,狀態儲存時間可以分別設定。
但在轉變到 SQL 的時候,會產生一些問題。基於此,我們舉個例子,來看一看為什麼運算元並行度和傳輸方式不可控了。
比如使用者定義了一個 UDF 函式,用來處理 Kafka 資料來源的某一個日誌,然後將這個處理後的資料寫入下游的 MySQL 或者其他儲存。我們假定 Kafka 某一個 Topic 分割槽有 10 個,整個任務的並行度設定為 20。這個時候就會發現,UDF 實際只會處理 10 個並行度的資料。Flink SQL 需要怎樣才能擴充呢?
針對這種情況,我們當前的解決方案是提供對執行圖編輯的功能,按照編輯結果同 SQL 一起儲存。如下圖所示,有三個 Operator,Data Source Operator 的 ID=1,UDF Operator 的 ID=2,Data Sink Operator 的 ID=3。
在這個過程中,將整個作業的並行度設為 20,Source 源 Operator1 的並行度設定為 10,1 和 2 之間的傳輸方式設為 rescale。然後在後端接收到後,同步將 Job Graph 進行修改,就會得到如下的執行圖,使用者就能夠比較好的解決掉這個問題了。
對於這個問題,未來我們的改進思路是透過 SQL 利用 Hint 功能來實現,或者更加智慧化一點,根據作業指標資訊,自動探測反壓節點,自動化設定,來降低使用者的使用成本。
對於 Create View 邏輯檢視的含義是指什麼呢?我也用一個案例來加以說明。從下圖可以看到,使用者自定義了一個 UDF 函式模擬了一個資料來源。我們將這個資料進行解析,建立 Create View,比如叫 Row Table,然後向下遊兩個目標表 SinkTable1 和 SinkTable2 寫入。最後看執行圖,會發現 UDF 函式被執行了兩次。
目前我們針對這一個問題收集並提供了一些解決方案。但在提供解決方案之前,我想先闡述一下這個問題產生的原因。Flink SQL 利用 Apache calcite 進行 SQL 語法解析,然後將解析後的 SQL 轉換成一個語法樹,經過 Flink Planner 生成 RealNode,經過 Optimizer Rule 進入 Codegen 環節。之後實際程式碼會有一個 Physical Plan 的過程,經過 Optimizer 形成 Steam Graph,然後轉化成 Job Graph,最終轉化成 Execution Graph。
那麼 View 是在哪一層級丟失的呢?其實是在 Apache calcite 語法解析的時候,View 它只是一個邏輯輔助,在這一過程會將其丟棄。那麼我們如何讓 View 這一資訊被底層感知到呢?
主要有兩個辦法:
- 辦法一是 SQL 解析的時候不丟失 View 資訊
- 辦法二是在 RealNode 到 Optimizer Rule 能夠識別到 View 的特徵資訊,這樣就可以把 View 當成一個真正的程式碼去翻譯了
辦法一是一個非常好的解決辦法,但是需要對 Apache calcite 進行很多改動,實現難度比較大,成本也比較高,所以採用了辦法二。最終的方案是採用識別特定函式實現,內建了一個 breakpoint 函式。在建立 View 的時候可以同時多 select 一個 breakpoint,這樣在底層翻譯的時候,就可以把它當成一個真正的 RealNode 處理。這個問題,未來我們是也是希望透過 SQL 利用 Hint 功能來實現。
對於狀態的儲存時間方面我們要怎麼處理呢?以資料流關聯 MySQL 分庫分表的資料舉例。常見的解決方案是利用 Flink CDC 將 MySQL 中的分庫分表資料,抽取寫入下游的 KV 儲存中,然後再透過另一個 Flink SQL 任務接入 Kafka 關聯,用時態表 Join 的方式將資料打寬,最終輸出結果。
這一過程可能會有兩個問題。第一,引入 HBase,我們的任務就會從一個拆分成兩個。其次需要假定下面這條鏈路的速度快於流的速度,否則上面 Topic 的資料到達的時候,而維表的資料還沒到達就關聯不上。那麼怎樣去解決這個問題,也是我們思考的地方。
我們採用的方案是用 Flink SQL+CDC+Regular Join 的方式來實現。接入還是一樣消費 Kafka,透過 CDC 來消費資料庫分庫分表的資料,最後透過正常的 Regular Join 來實現。
這裡的 Regular join 底層同時依賴兩個 MapState,比如 Topic A 對應 MapState 是 A,MySQL 裡的資料庫的資料對應的是 B。如果我們能輕易的將 MapState B 的狀態設定為 0 或者不過期,那麼這個狀態的資料就會被永久的儲存下來。即使流的資料先到達了,後面狀態資料到達也能觸發資料的關聯,從而比較好的解決這類問題。
具體的解決辦法是,我們可以在 Flink SQL 中指定左右流 Join 的狀態時間,在 Graph 中識別有 Join 的運算元,最終透傳到 Join 運算元做狀態時間的設定。
任務開發完成,需要多少資源呢?線上流量波動,出現延遲怎麼辦?任務越來越多或任務併發調整,資源不足怎麼辦?
針對這些問題,我們對應的解決辦法主要包含:靜態資源調優、動態資源調優及擴縮容、資源彈效能力的建設。那麼具體我們是怎麼做的呢?下面請大家跟著我來一起來看一看。
舉個例子,任務終於開發完成,透過了任務校驗,但是任務引數,比如並行度、Slot、記憶體……該給多少才能正常執行呢?提供瞭如下三種 case:
- Case1:資源直接給足-->正常執行-->結束--->資源浪費
- Case2:資源不足-->反壓或者延遲嚴重-->反覆調整資源-->費時費力
- Case3:指標計算 Groupby-->託管記憶體不足/增量 Checkpoint 沒開-->任務執行一段時間失敗
綜上所述,三個案例的共性是任務調優成本高,且對使用者本身有一定的能力要求。對此我們專門做了靜態資源調優的解決辦法。
假定使用者開發了一個 Flink SQL,第一個環節,首先進行語法校驗,然後透過語法校驗及後端生成 Stream Graph,拿到 Stream Graph 的同時我們還會進行 Source/Sink 連通校驗和引數初步調整。
第二個環節,根據當前的任務邏輯及流量合理的調整資源。首先探測 Source 的流量,然後拿這個值和使用者的作業 SQL、Stream Graph 做 Optimizer。Optimizer 部分主要包括 Restart、HighAvailable、Checkpoint、Parallelism、TaskManager、JobManager、StateBackend。
透過不斷最佳化,得到一個比較好的任務資源引數,供使用者作為初始任務資源使用。如果探測的資源流量較大,Sink 到 MySQL 的 Batch 設定較小,針對這種情況,我們會提醒 SQL 當中的引數進行調整,來幫助使用者更好的調整 SQL 任務的引數。
最終我們會給使用者提供給兩個檢視,分別是 SQL 本身調整的預覽、任務所依賴引數的調整預覽。如果使用者覺得 ok,就可以按照當前的引數上線執行了。以上是靜態資源調優。
那麼任務上線後是什麼情況呢?比如 Flink SQL 正常的 Running,首先將指標採集 Push 到 Kafka,然後會有實時任務進行指標的清洗聚合。針對重要的指標,比如消費延遲指標、運算元速率指標、JVM 程式指標,狀態大小指標等。
這些指標作為動態資源調整服務的入參,能及時感知到當前任務的執行狀況,然後動態資源調整會進行需求資源的申請,將任務重啟,並給使用者傳送通知。如果重啟失敗,會進行配置回滾,然後告知使用者調整失敗需要手工介入。
針對動態資源調整,我們的場景大概有如下四個:
- 設定歷史資料追數:Kafka 積壓歷史資料初次消費、CDC 全量到增量。
- 期望時間動態調整:特定時間擴縮容,解決活動可預知的流量高峰。
- 根據指標動態調整:延遲或反壓及時調整,預測流量變化提前調整。
- 異常指標動態調整:例如 JVM GC 頻繁,及時調整 TM 記憶體。
如上就是我們想做的動態資源調優,最終實現的效果及具體的做法。
下面進入彈性資源能力的建設。過去我們基於 Yarn On ECS 的方式,在擴容的時候需要較長的時間。目前我們基於 Yarn On K8s 來實現的,在 Yarn Label 上我們會進行三種佇列的設定打標籤,固定資源佇列對應的是正式任務;彈性資源佇列對應的是突發流量任務;搶佔資源佇列對應的是測試任務。
如果突然線上流量波動,當前任務的固定資源不足。那麼我們就可以將透過分鐘級的時效,將彈性資源佇列資源擴出來,然後將任務排程上去。這樣就避免了突發流量所帶來額外資源的消耗,同時我們也不需要按照最高峰值流量去預估資源,只需按照常定的任務資源數量來設定底層所需要的資源。
未來我們將引進 Flink Native K8S,希望藉助 K8s 本身的資源管理能力提供資源彈性使使用者有較好的體驗。
指標體系在 Flink 任務中至關重要,主要包含任務可觀測、動態資源調優和擴縮容、排程任務依賴三個方面。
- 第一,任務可觀測方面,我們的做法是採集指標到 Kafka,然後透過 Flink 清洗聚合寫入 Influxdb/MySQL,Grafana 展示/指標異常監控告警。
- 第二,動態資源調整和擴縮容的指標應用已經前面說明,就不再贅述了。
- 第三,排程任務依賴,是指 Kafka/MysqlCDC 資料入湖,下游有離線排程依賴,我們需要感知當前任務是否有延遲,Checkpoint 有沒有做,資料在數倉裡是否具有可見性,還需要保證資料完整入倉入湖後,下游任務才會啟動。
分享兩個場景。第一個場景,日誌場景建設。當資料量大,入倉時間多於 10 分鐘的時候,下游任務相應增大,有沒有辦法縮短入倉時間?當 HDFS 寫入流量波動較大的時候,能不能更加平穩,且資料不丟不重?
眾所周知,從日誌檔案透過 Kafka 到 Flink SQL、寫入 Iceberg 都有可能產生資料重複,這一鏈路能保證資料不丟,但較難保證資料不重。
對此我們的方案是基於檔案日誌採集 MetaData Logs,然後將 MetaData Logs 在下游複用。其中 MetaData Logs 的檔案的行數起到很重要的作用,因為這一鏈路能保證資料不丟。
如果資料的行數等於 MetaData Logs,就代表這個資料沒有重複,一旦資料行數多於 MetaData Logs,就代表這個資料有重複了,但我們只需要基於重複的某一個檔案日誌進行去重處理,而不需要對全量日誌檔案都進行去重處理。基於這樣處理方式,我們發現入倉時效從原來的 10-20 分鐘,降低到分鐘級別的延遲。同時這一鏈路也能保證入倉資料不丟不重,直接可用,等同於離線日誌拉取 ETL 的場景。
針對 Iceberg 表我們建立了 Iceberg Manager 來做小檔案合併、過期快照清理、孤兒檔案清理。
第二個場景,資料庫場景建設。比如資料庫是 MySQL,我們想透過 Flink CDC 將資料直接寫入 Iceberg V2 表。那麼就會有如下幾方面的考慮:
- 多個 Flink CDC 任務是否會對一個 MySQL 讀取?資料庫是否會有壓力?已經讀取的資料能否複用起來?
- Flink CDC 增量讀取,支援指定讀取的時間起點。
- IcebergV2 全量資料同步時,資料量較大,容易產生了較多 Delete Files,輔助鏈路的 Iceberg Manager 在進行表級別最佳化的時候,就會產生較大的壓力。
- Flink CDC 同步任務太麻煩,希望配置化就生成好任務,希望有一鍵資料入湖的能力。
基於此,我們做了一個鏈路的輔助,一鍵任務生成。輔助自動任務的調優擴縮容機制,保證 Flink CDC 全量同步和增量同步資源的切換問題,透過 Kafka 來實現對同一個資料來源讀取時候的壓力問題,將資料寫入 Kafka,Kafka 的資料會被下游的 Flink SQL 任務自動感知並同步。
為了解決 Delete Files 全量資料過多的問題。我們在進行全量同步的時候,會關閉寫入 Iceberg V2 表的 upsert 功能,在增量的時候才會開啟,這樣就可以保證全量同步的時候資料既不丟也不重。同時,Flink SQL 任務增量資料會寫入 Iceberg V1 表,方便下游鏈路進行復用。
三、未來展望
未來 Flink SQL 或者平臺建設將圍繞以下四個方面進行展開:
- 第一,批流一體。大資料離線數倉和實時數倉分為兩套系統,一般離線數倉透過 Spark、Hive 來實現,實時數倉使用 Flink。隨著 Flink 批處理能力的不斷建設,我們認為使用一套批流一體,既能降低使用者成本,還能更方便的避免兩套引擎所帶來的指標含義不同的影響。
- 第二,資源彈效能力的建設。未來會基於 K8s 不斷引進彈性資源能力,更好的提供給使用者使用。
- 第三,使用場景的建設,結合 Flink SQL 基於 Kafka 提供延遲訊息的功能。
- 第四,近實時數倉 TableStore 的建設。TableStore 新版本釋出,計劃先實踐起來,同時還將結合 Iceberg 不斷探索實踐,實現讓大家基於近實時數倉,就能夠得到時效性和確定性兩種融合的效果。
更多內容
活動推薦
阿里雲基於 Apache Flink 構建的企業級產品-實時計算Flink版現開啟活動:
99 元試用 實時計算Flink版(包年包月、10CU)即有機會獲得 Flink 獨家定製衛衣;另包 3 個月及以上還有 85 折優惠!
瞭解活動詳情:https://www.aliyun.com/produc...