摘要:本文整理自阿里雲 Flink 儲存引擎團隊負責人,Apache Flink 引擎架構師 & PMC 梅源在 FFA 核心技術專場的分享。主要介紹在 2022 年度,Flink 容錯 2.0 這個專案在社群和阿里雲產品的進展。內容包括:
- Flink 容錯恢復 2.0 專案簡介及思考
- 2022 年度 Flink 容錯 2.0 專案進展
一、Flink 容錯恢復 2.0 專案簡介及思考
首先來概括的看一下 Flink 容錯的鏈路,主要包括四個過程:做快照(Checkpointing),發現失敗節點(Failure Detection),重新排程(Re-Scheduling)和狀態恢復(State Recovery)。
在一個 Flink 作業中,資料從資料來源經過各個運算元的處理最終寫入 Sink。其中有些運算元需要記錄資料處理的中間結果,會暫時把中間結果快取在運算元內部,即運算元的狀態中。如果這個 Flink 作業因為各種原因出現失敗或者錯誤,就需要重新恢復這些狀態,因此我們需要對狀態進行週期性快照,即 Checkpointing 過程。由於快照很頻繁,所以需要 Checkpointing 過程穩定、輕量並保證成功。
如果一個節點掛了,需要快速發現失敗節點,並完成相應的清理工作。
然後生成新的作業,並重新排程,完成部署。
當作業重新排程之後,需要從最新的快照中恢復運算元的中間狀態,也就是 State Recovery 狀態恢復。
從上面的描述不難看出,容錯恢復是一個全鏈路的過程,包括給狀態做快照,發現節點失敗,重新排程部署以及恢復狀態。另一方面容錯恢復也是個需要從多個維度來考慮的問題,包括容錯成本、對正常處理的影響、資料一致性保證的程度、以及在整個容錯恢復過程中的行為表現(比如是否需要滿速 TPS 的保證和不斷流的保證)等等。另外需要指出的是在雲原生背景下,容器化部署帶來了一些新的限制和便利,這些都是我們在設計新一代容錯時不能忽視的地方。有關這個部分更詳細的內容可以參考去年的 talk “Flink 新一代流計算和容錯——階段總結和展望”
2022 年我們的工作主要集中在 Checkpointing,Scheduling 和 State Recovery 這三個部分。在 Checkpointing 這個部分,我們實現了分散式快照架構的升級:
- Flink 1.16 修復了和 Unaligned Checkpoint 相關的幾個關鍵 bug,特別是 Unaligned 和 Aligned Checkpoint 之間轉換這個部分,使得 Unaligned Checkpoint 可以真正做到生產可用;
- Flink 1.16 釋出的一個重要 feature 是通用增量 Checkpoints,透過增量快照和 State Store 快照過程的分離,可以保證穩定快速的 Checkpointing 過程。這個後續我們會有相關 Blog Post 詳細的分析通用增量 Checkpoints 在各種 Benchmark 下的各項指標以及適用場景。
在 Scheduling 這個部分,Approximate 和 At-least-once 單點重啟功能已經在阿里雲實時計算企業級服務裡實現,主要由阿里雲實時計算 Flink Runtime 團隊完成,他們還透過引入作業熱更新功能,極大縮短了擴縮容的斷流時間。這兩個功能在阿里雲實時計算 VVR 6.0.4 版本上線,歡迎大家試用。
在 State Recovery 這個部分,社群在 1.15 引入了工作目錄的概念,結合 K8S 的 PV 掛載,可以實現狀態的本地恢復。為了適應雲原生部署,我們在狀態儲存部分進行了分層儲存架構升級,對恢復和擴縮容都有很大的改進。特別是 Lazy-Load 的引入,使得快速恢復不再受狀態大小的影響,結合上面提到的作業熱更新功能,基本可以做到擴縮容無斷流。Lazy-Load 這個功能也已在阿里雲 VVR 6.x 版本中上線。
下面我主要從快照生成,作業恢復/擴縮容,快照管理這三個方面詳細介紹 2022 年度 Flink 在容錯部分的進展。
二、2022 年度 Flink 容錯 2.0 專案進展
2.1 最佳化快照生成
在快照生成部分,我們對 Flink 分散式快照架構進行了整體升級。
先來看看升級前的架構有什麼問題:
問題 1:對齊時間長,反壓時被完全阻塞
Flink 的 Checkpoint 機制是透過從 Source 插入 Barrier,然後在 Barrier 流過每個運算元的時候給每個運算元做快照來完成的。為了保證全域性一致性,如果運算元有多個輸入管道的時候,需要對齊多個輸入的 Barrier。 這就產生了問題 1,因為每條鏈路的處理速度不一樣,因此 Barrier 對齊是需要時間的。如果某一條鏈路有反壓,會因為等待對齊而使得整條鏈路完全被阻塞,Checkpoint 也會因為阻塞而無法完成。
問題 2:Buffer 數目固定,管道中有多餘的處理資料
由於運算元間的上下游 Buffer 數目是固定的,它們會快取比實際所需更多的資料。這些多餘的資料不僅會在反壓時進一步阻塞鏈路,而且會使得 Unaligned Checkpoint 儲存更多的上下游管道資料。
問題 3:快照非同步上傳時間較長且不可控
快照的過程包括兩部分:同步狀態刷盤和非同步上傳狀態檔案,其中非同步檔案上傳的過程和狀態檔案大小相關,時間較長且不可控。
我們從 Flink 1.11 開始著手逐一的解決這些問題,如下圖所示。
Flink 1.11、 Flink 1.12 引入了 Unaligned Checkpoint, 使得 Checkpoint Barrier 不被緩慢的中間資料阻塞。Flink 1.13、Flink 1.14 引入了 Buffer Debloating,讓運算元與運算元間的管道資料變得更少。Flink 1.15、Flink 1.16 引入了通用增量 Checkpoints,讓非同步上傳的過程更快、更穩定。
升級後的分散式快照架構如下圖所示:
對於問題 1,在 Flink 1.16 版本中,Unaligned Checkpoint 允許透支 Buffer,解決了在 Buffer 不足時,不能及時響應 Unaligned Checkpoint 的問題。 此外,全域性計時超時機制的引入能夠有效改進 Unaligned 和 Aligned Checkpoint 之間自動轉換的觸發條件。
對於問題 2,Buffer debloating 的引入可以動態調整快取的資料量,動態快取 1 秒內需要處理的資料。
下面我們來重點看一看第 3 個問題是如何用通用增量 Checkpoint 來解決的
Flink 的運算元狀態更新會反映在狀態表中。在之前的設計當中,Flink 運算元做快照的過程分為兩步:第一步是同步的對狀態表進行快照,記憶體中的資料刷盤,準備好上傳到持久儲存的檔案;第二步是非同步的上傳這些檔案。
非同步上傳檔案這個部分有兩個問題:
問題 1:非同步上傳的檔案大小依賴 State Backend 的實現
問題 2:非同步過程需要等到同步過程結束才能開始,因為同步快照結束前是沒法準備好需要上傳的檔案的
我們來分別看一下這兩個問題。對於第一個問題,以 RocksDB 為例,雖然 Flink 對 RocksDB 也支援增量 Checpoint,但是 RocksDB 出於自身實現考慮,它需要對檔案做 Compaction。每次 Compaction 會產生新的比較大的檔案,那這個時候即使是增量 Checkpoint,需要上傳的檔案也會因此時不時變大。在 Flink 作業併發比較大的情況下,上傳檔案時不時變大的問題就會變得很頻繁,因為只有等所有併發的檔案上傳完畢,一個完整的運算元狀態才算快照完成。
對於第二個問題,在同步快照結束前,Flink 無法準備好需要上傳的檔案,所以必須要等快照結束時才能開始上傳。也就是說,上圖中的紅色斜條紋這個時間段完全被浪費了。如果需要上傳的狀態比較大,會在很短時間內對 CPU 和網路產生較大的壓力。
為了解決上述兩個問題,我們在 Flink 社群實現了通用增量快照。在新架構下,狀態更新不僅會更新狀態表,而且會記錄狀態的更新日誌。上圖中狀態表會和架構升級前一樣週期性的刷到持久儲存,但是這個週期可以比較大(比如 10 分鐘)在後臺慢慢上傳,該過程稱為物化過程。同時狀態更新日誌也會持續上傳到遠端持久儲存,並且在做 Checkpoint 時 Flush 剩餘全部日誌。
這樣的設計就比較好的解決了前面提到的兩個問題:透過將快照過程和物化過程完全獨立開來,可以讓非同步上傳的檔案大小變得很穩定;同時因為狀態更新是持續的,所以我們可以在快照之前就一直持續的上傳更新日誌,所以在 Flush 以後我們實際需要上傳的資料量就變得很小。
架構升級後的一個 Checkpoint 由物化的狀態表以及增量更新的日誌組成。物化過程結束後,相對應的更新日誌就可以被刪除了。上圖中的藍色方框部分,是通用增量快照和之前架構的區別,這個部分被稱為 Changelog Storage(DSTL)。
DSTL 是 Durable Short-term Log 的縮寫。我們從這個英文名就能看出來 DSTL 是有針對性需求的
- 需要短期持久化增量日誌,物化後即可刪除
- 需要支援高頻寫,是一個純 append 寫操作,僅在恢復時需要讀取
- 需要 99.9% 的寫請求在1秒內完成
- 需要和現有的 Checkpoint 機制提供同一級別的一致性保證
社群現在的版本是用 DFS 來實現的,綜合考量下來基本可以滿足需求。同時 DSTL 提供了標準的介面也可以對接其他的儲存。在本次 Flink Forward 美團的分享中,可以看到美團在使用 Bookkeeper 實現 DSTL 以及通用增量快照方面取得的效能的提升。
這個部分的最後我們來看一下使用通用增量快照的 Trade-off
通用增量快照帶來的好處顯而易見:
- 可以讓 Checkpoint 做的更穩定,平滑 CPU 曲線,平穩網路流量使用(因為快照上傳的時間被拉長了,並且單次上傳量更小更可控)
- 可以更快速的完成 Checkpoint(因為減少了做快照 Flush 的那個部分需要上傳的資料)
- 也因此,我們也可以獲得更小的端到端的資料延遲,減小 Transactional Sink 的延遲
- 因為可以把 Checkpoint 做快,所以每次 Checkpoint 恢復時需要回滾的資料量也會變少。這對於對資料回滾量有要求的應用是非常關鍵的
通用增量快照也會帶來一些額外的 Cost,主要來自兩個方面:Checkpoint 放大和狀態雙寫:
- Checkpoint 放大的影響主要有兩點。第一,遠端的儲存空間變大。但遠端儲存空間很便宜,10G 一個月大約 1 塊錢。第二,會有額外的網路流量。但一般做 Checkpoint 使用的流量也是內網流量,費用幾乎可以忽略不計。
- 對於狀態雙寫,雙寫會對極限效能有一些影響,但在我們的實驗中發現在網路不是瓶頸的情況下,極限效能的損失在 2-3% 左右(Flink 1.17 中最佳化了雙寫部分 FLINK-30345,也會 backport 到 Flink 1.16),因此效能損失幾乎可以忽略不計。
對於通用增量 Checkpoint 這個部分我們近期會有更詳盡的測試分析報告,敬請期待。
2.2 最佳化作業恢復和擴縮容
接下來講一講 Flink 社群在作業恢復和擴縮容部分的最佳化,主要包括最佳化本地狀態重建,雲原生背景下的分層狀態儲存架構升級,以及簡化排程過程。
作業擴縮容和作業容錯恢復有很多共性,比如都需要依據上一次快照來做恢復,都需要重新排程,但他們在細微之處又是有些區別的。
本地狀態重建
以狀態恢復本地重建來講,對於容錯恢復,將狀態檔案原樣載入進本地資料庫就可以了,但是如果是擴縮容恢復就會更復雜一些。舉例來說上圖中的作業併發從 3 擴容到 4,新作業 task 2 的狀態有一部分來自原先作業的 task 1,還有一部分來自原先作業的 task 2,分別是橙色和黃色部分。
Flink 作業運算元的狀態在 Rescaling 做狀態重新分配時,新分配的狀態來自原先作業相鄰的併發,不可能出現跳躍的有間隔的狀態分配。在縮容時,有可能有多個狀態合成一個新狀態;在擴容的時候,因為狀態一定是變小的,所以新的變小的狀態一定最多來自相鄰的兩個原先的併發。
接下來具體講一講狀態是如何做本地重建的,以 RocksDB 為例。
- 第一步,需要下載相關的狀態檔案。
- 第二步,重建初始的 RocksDB 例項,並刪除對例項無用的 Key,即刪除上圖中灰色的部分,留下橙色部分。
- 第三步,將臨時 RocksDB 例項中的 Key 插入到第二步重建的 RocksDB 中,也就是黃色的部分插入到橙色的 DB 中。
我們在 Flink 1.16 中,對本地重建的第二步進行了最佳化。透過引入 DeleteRange
,使得整個刪除無用 Key 的操作變成 O(1),並且因為最多隻可能有 2 個 Range 需要刪除,因此額外的資料結構(TombStone 表)對正常讀寫的影響微乎其微。
從上圖右邊的實驗結果可以看出,對於狀態大小 122GB 的 Word Count 作業,從併發 3 擴容到併發 4,Flink 1.16 比之前的版本擴容速度提升 2 – 10 倍。同時我們在 Flink 1.16 引入了標準的 Rescaling Micro Benchmark,在此之前社群沒有一個標準來測試 Rescaling 的效能。
在阿里雲實時計算企業版中,我們對本地狀態重建的第一步和第三步也進行了最佳化。我們只需要下載需要的狀態檔案,並且可以進行檔案粒度的直接合並,避免建立臨時 DB 例項。阿里雲實時計算版本和 Flink 1.16 社群版本對比,縮容速度也有 7 倍的提升。
分層狀態儲存架構
為了更好的適應雲原生的大背景,我們對分層狀態儲存架構也進行了初步探索,也就是說我們把遠端盤也作為 State Backend 的一部分。這種分層架構可以解決 Flink 狀態儲存在雲原生背景下面臨的大部分問題:
- 解決容器化部署本地磁碟大小受限的問題
- 解決外接狀態成本高,資料一致性難以保障的問題
- 解決小狀態需要額外落盤的問題
- 解決大狀態訪問速度慢的問題
這些問題和容錯沒有太大關係,以後有機會專門講一講這個部分。
遠端盤作為 State Backend 的一部分,狀態載入策略可以變得更靈活。這樣狀態在沒有完全載入恢復完成之前,就可以開始資料處理。在最佳化前,使用者狀態恢復時,讀寫被完全阻塞。在最佳化後,使用者狀態恢復的過程中,可以進行半速讀寫,然後逐漸恢復到全速,如上圖所示。
可配置的狀態載入策略的引入,極大的縮短了狀態恢復的初始延遲和業務斷流時間。我們從下面兩個實驗可以看到,單併發狀態大小 7GB 左右的作業啟動後延遲時間在最佳化後從 4.5 分鐘降到了 1.25 分鐘,狀態恢復部分提速 75%。同樣的作業,在最佳化後可以發現從狀態恢復開始時,就會有 TPS,極大的降低了業務斷流的時間。
在作業排程這個部分,阿里雲 Flink Runtime 團隊在阿里雲實時計算 VVR 6.0.4 版本中引入了作業熱更新這個功能,其核心思想是在擴縮容的時候簡化作業重新排程的步驟,並且在新的作業生成後再停掉老的作業,這樣可以進一步縮短作業斷流的時間。在沒有資源預申請的情況下,作業熱更新可以使無狀態作業擴容和縮容時間降低三倍左右。
綜上所述,透過延遲狀態載入策略,配合作業熱更新,基本可以保證在狀態恢復和排程層面,做到擴縮容無斷流或極短時間斷流。
2.3 最佳化快照管理
前兩個部分都是講效能上的最佳化,最後一個部分我想聊一聊快照管理部分的一些梳理。Flink 發展到現今已經是一個很成熟的系統了,清晰化的概念以及簡單易用性是衡量一個系統成熟度的很重要的部分,所以這裡聊一聊快照概念和管理。
Flink 的快照 Snapshot 分為兩種:Savepoint 和 Checkpoint。
Savepoint 一般由使用者觸發,所以它歸屬使用者所有,因此由使用者負責建立和刪除。正因此,Flink 系統引擎層是不能夠去刪除 Savepoint 相關檔案的。所以 Savepoint 不和 Flink 作業強繫結,不同的 Flink 作業可以從同一個 Savepoint 啟動。Savepoint 是自包含的:自己包含所需要的一切。
Checkpoint 正好相反,它的主要作用是系統容錯自愈,所以它由 Flink 引擎週期性觸發,並且所屬權歸屬 Flink 引擎。Checkpoint 檔案的組織結構都由 Flink 引擎決定和管理,所以引擎負責按需清理 Checkpoint 檔案。正因此,Checkpoint 和生成該 Checkpoint 的作業強繫結,並且是非自包含的,比如說 Incremental Checkpoint 之間會有依賴關係。
那有什麼問題呢?因為 Savepoint 主要目標服務物件是使用者,為了對使用者友好,Savepoint 使用使用者可讀的標準格式,也正因此 Savepoints 做得非常慢,經常情況下狀態稍微大一點就會超時,同樣恢復也很慢。另一方面,Checkpoint 使用的是增量系統原生格式,所以做得很快。
這種情況下,使用者會把 Retained Checkpoint 當成 Savepoint 來使用。Retained Checkpoint 是在作業停掉後保留的 Checkpoint,這樣Retained Checkpoint 就變成了 Savepoint 和 Checkpoint 的混合體。造成的問題是使用者負責刪除 Retained Checkpoint,但是使用者並不知道如何安全的刪除 Retained Checkpoint。
為了解決上述問題,Flink 1.15 引入了兩種狀態恢復模式,即 Claim 模式和 No-Claim 模式。
在 Claim 恢復模式下,引擎宣告 Retained Checkpoint 的所屬權,Retained Checkpoint 歸引擎所有,引擎負責刪除。
在 No-Claim 恢復模式下,引擎放棄 Retained Checkpoint 的所屬權。Retained Checkpoint 中所有的檔案都不會被 Flink 引擎使用,使用者可以很安全的刪除 Retained Checkpoint。
在 No-Claim 的基礎上,我們引入了 Native Savepoint,來加速 Savepoint 的建立和恢復。Native Savepoints 使用和 Checkpoint 一樣的儲存格式,其實現原理和 No-Claim 類似。Savepoint 不會使用之前的 Checkpoint 檔案,相當於做一個全量的 Checkpoint。我們的企業版本透過進一步最佳化,讓 Native Savepoint 也真正能做到增量 Savepoint。
上圖是 Flink 社群引入 Native Savepoint 之前,之後以及企業版進一步最佳化後的 Savepoint 效能對比圖。我們可以看到在引入 Native Savepoint 之前,Savepoint 做的很慢。在 Savepoint 總大小 5GB 的情況下(狀態中等大小),做一次 Savepoint 超過 10 分鐘(超時)。這意味著一旦使用者使用 stop-with-savepoint
來停止作業(也就是在停止作業前做個 Savepoint),就得等超過 10 分鐘,完全沒法用。在引入 Native Savepoint 之後,需要等 2 分半鐘,也比較長,勉強能用。企業版進一步最佳化後,等待時間變成 5s 鍾,這個基本上在可等待的範圍之內了。
最後我們小結回顧一下 Flink 容錯恢復在 2022 年的主要進展
- 在分散式快照架構方面,Unaligned Checkpoint 引入全域性計時器,可以透過超時機制自動從 Aligned Checkpoint 切換成 Unaligned Checkpoint,這個對於 Unaligned Checkpoint 生產可用是非常重要的一步
- 通用增量 Checkpoint 生產可用,這對於 Checkpoint 穩定性和完成速度有很大的提升,同時可以平滑 CPU 和網路頻寬的使用
- 這裡值得一提的是,不僅僅是阿里巴巴在 Checkpoint 這個部分貢獻了大量的程式碼,很多其他的公司也積極的投入到社群當中,比如 Shopee 和美團。他們在社群中貢獻程式碼同時,也積極推動這些功能在公司內部的落地和延展,取得了不錯的效果
- 在狀態儲存方面,我們進行了分層狀態儲存的初步探索,擴縮容速度有 2 – 10 倍的提升
- 阿里雲實時計算平臺推出了擴縮容無斷流的組合功能:延遲狀態載入和作業熱更新,分別從狀態載入和作業排程這兩個方面來實現擴縮容無斷流
- 引入增量 Native Savepoint,全面提升 Savepoint 的可用性和效能。
更多內容
活動推薦
阿里雲基於 Apache Flink 構建的企業級產品-實時計算Flink版現開啟活動:
99 元試用 實時計算Flink版(包年包月、10CU)即有機會獲得 Flink 獨家定製衛衣;另包 3 個月及以上還有 85 折優惠!
瞭解活動詳情:https://www.aliyun.com/produc...