Flink 實踐 | Flink 2.0 狀態(tài)存算分離改造實踐
1. Flink 大狀態(tài)管理痛點 2. 阿里云自研狀態(tài)存儲后端 Gemini 的存算分離實踐 3. 存算分離的進一步探索 4. 批量化存算分離適用場景
Tips: 點擊 「閱讀原文」 在線觀看 FFA 2023 會后資料~
01
Flink 大狀態(tài)管理痛點
1.1 Flink 狀態(tài)管理

狀態(tài)管理是有狀態(tài)流計算的核心。目前在 Flink 生產(chǎn)環(huán)境中使用的最多的狀態(tài)后端是基于 RocksDB 的實現(xiàn),它是一個以本地磁盤為主的狀態(tài)管理,將狀態(tài)文件存儲于本地,同時在進行檢查點的時候將文件周期性寫入 DFS 。這是一種存算一體的架構,它足夠簡單,在小狀態(tài)作業(yè)下能夠保證穩(wěn)定高效,可以滿足絕大部分場景的需求。隨著 Flink 的發(fā)展,業(yè)務場景日益復雜,大狀態(tài)作業(yè)屢見不鮮,在存算一體的架構下涌現(xiàn)了很多與狀態(tài)管理有關的現(xiàn)實問題。
1.2 大狀態(tài)作業(yè)痛點

大狀態(tài)作業(yè)下,基于 RocksDB 本地磁盤存算一體的狀態(tài)管 理主要會遇到以下四方面的問題:
-
本地磁盤有可能會出現(xiàn)空間不足的情況,通常解決這類問題的方法就是擴容。在目前集群部署或是云原生部署的模式下,單獨進行本地盤的擴容是不方便的,所以用戶一般會增加并發(fā)度,也就是涉及到存儲和計算綁在一起進行擴容,加劇了計算資源的浪費。
-
作業(yè)正常狀態(tài)訪問時,本地磁盤 I/O 也會遇到一些瓶頸。這導致作業(yè)整體性能不足,同樣需要進行擴并發(fā)操作。
-
檢查點的開銷比較大。由于狀態(tài)非常大,在檢查點期間對于遠端存儲訪問量呈現(xiàn)一個尖峰態(tài)勢。
-
在作業(yè)恢復的時候,需要從遠端讀取全量文件到本地,這個過程也十分緩慢。
上述前兩點是影響用戶成本的問題,而檢查點的開銷與恢復速度是 Flink 中影響易用性的關鍵問題。
1.3 存算分離的架構

對于以上問題,我們提出了存算分離的架構來解決。存算分離可以擺脫本地磁盤的限制,將遠端存儲(DFS)作為主存儲,同時將空閑的本地磁盤作為一個 Cache 來進行使用。同時用戶仍可以選擇本地磁盤作為主存儲,還用原來的模式來運行。這樣做的顯著的好處是,一方面由于磁盤空間和 I/O 性能不足的問題不再影響計算資源,另一方面是狀態(tài)檢查點與恢復在遠端就可以直接完成,變得更加輕量級。從架構上完美解決了大狀態(tài)作業(yè)面臨的問題。
02
阿里云自研狀態(tài)存儲后端 Gemini 的存算分離實踐
在進入存算分離架構探討的最開始,我希望先從阿里云自研的企業(yè)級狀態(tài)存儲 Gemini 入手,探尋它在存算分離上的一些實踐,主要分為以下三項:
2.1 多種文件系統(tǒng)分層管理

Gemini 能夠把遠端作為狀態(tài)主存儲的一部分。它首先將狀態(tài)文件存儲于本地磁盤,如果本地磁盤不足,則將文件移動到遠端存儲。本地磁盤中存留的是訪問概率高的文件,遠端存儲的是不容易訪問的文件。兩部分共同構成了主存儲,并在此基礎上進行了冷熱劃分,保證了在給定資源條件下的高效服務。Gemini 的這種文件分層管理模式擺脫了本地磁盤空間的限制。理論上本地空間可以配置為零,以達到純遠端存儲的效果。
2.2 狀態(tài)懶加載

Gemini 能夠支持遠端文件存儲,在作業(yè)恢復的場景之下,無需將數(shù)據(jù)從遠端文件加載回本地就可以開啟服務,使用戶作業(yè)進入運行狀態(tài)。這一功能稱為狀態(tài)懶加載。在實際恢復過程中,Gemini 僅需將元數(shù)據(jù)以及少量內(nèi)存中的數(shù)據(jù)從遠端加載回,就可以重建整個存儲并啟動。
雖然作業(yè)已經(jīng)從遠端文件啟動了,但讀取遠端文件涉及到更長的 I/O 延遲,性能仍舊不理想,此時需要使用內(nèi)存和本地磁盤進行加速。Gemini 會使用后臺線程進行異步下載,將未下載的數(shù)據(jù)文件逐漸轉移至本地磁盤。下載過程分為多種策略,比如按照 LSM-tree 層次的順序,或者按照實際訪問的順序來下載。這些策略可以在不同場景進一步縮短從懶加載恢復到全速運行性能的時間。
2.3 Gemini 延遲剪裁

在改并發(fā)的場景中,比如將兩個并發(fā)的狀態(tài)數(shù)據(jù)合并成一個并發(fā)時,目前 RocksDB 是把這兩份數(shù)據(jù)都下載完成之后再做一個合并,涉及到將多余的數(shù)據(jù)剪裁掉,重建數(shù)據(jù)文件,其速度是比較慢的。社區(qū)針對這個過程進行了很多的針對性優(yōu)化,但仍然避免不了數(shù)據(jù)文件的下載。Gemini 只需要把這兩部分數(shù)據(jù)的元數(shù)據(jù)進行加載,并且把它們合成一個特殊的 LSM-tree 結構,就可以啟動服務,這一過程稱為延遲剪裁。
重建后 LSM-tree 的層數(shù)相比正常情況下會比較多。比如針對圖中的例子,有兩個 L0 層,兩個 L1 層和兩個 L2 層。由于 Flink 有 KeyGroup 數(shù)據(jù)劃分的機制存在,層數(shù)變多并不會對讀鏈路長度造成影響。由于并未對數(shù)據(jù)進行實際的裁剪,會存在一些多余的數(shù)據(jù),這些數(shù)據(jù)會在之后的整理 (Compaction) 過程逐步清理掉。延遲剪裁的過程無需對數(shù)據(jù)本身進行下載和實際合并操作,它可以極大地縮短狀態(tài)恢復的時間。
2.4 Gemini 恢復效果

有了異步剪裁狀態(tài)+狀態(tài)懶加載,對于 Gemini 來說,恢復時間即作業(yè)從 INITIALIZING 到 RUNNING 的狀態(tài)可以變得非常之短,相比于本地狀態(tài)存儲的方案是一個極大的提升。

我們針對 Gemini 與 RocksDB 的改并發(fā)時間進行了評測。評測的指標為從作業(yè)啟動一直到恢復原有性能的時間,這包含了 Gemini 異步下載文件的時間。從上述實驗結果中可以看到 Gemini 相比于RocksDB 在縮容、擴容的場景下都有明顯的提升。
03
存算分離的進一步探索

Gemini 做存算分離相關的優(yōu)化部分解決了前述大作業(yè)場景的問題。本地空間不足的問題可以通過遠端空間來解決。針對檢查點開銷大的問題,因為已經(jīng)有一部分文件遠端存儲上了,無需再次上傳,這部分的開銷也得以減少。針對作業(yè)恢復慢的問題,狀態(tài)懶加載+延遲剪裁功能,使得作業(yè)能夠快速的恢復運行狀態(tài)。
這里還有一個功能是對 Memtable 的快照。Gemini 在做檢查點的時候,是將 Memtable 的原樣上傳到遠端存儲上,不會影響 Memtable flush 的過程,也不會影響內(nèi)部的 Compaction。它的效果和通用增量快照的 changelog 的效果是類似的,都會緩解檢查點時的 CPU 開銷和 DFS I/O 量的尖峰。
3.1 Gemini 存算分離的問題

Gemini 在存算分離方面做了不錯的實踐,在阿里內(nèi)部與云上客戶的大狀態(tài)作業(yè)場景下均取得了不錯的效果。但它仍存在著一些問題:
第一個問題,Gemini 還是把本地磁盤作為主存的一部分,狀態(tài)文件是優(yōu)先寫到本地磁盤的,這并非最徹底的一個存算分離。這樣會導致檢查點時上傳文件數(shù)量還是會比較多,持續(xù)時間較長,做不到非常輕量級的檢查點。
第二個問題,是所有存算分離方案都會遇到的一個問題,就是與本地方案的性能差距。目前的方案中 Gemini 已經(jīng)利用了本地磁盤,但本地磁盤的利用效率并不是最高的。如果更多的請求可以落到內(nèi)存或者本地磁盤,對應的遠端 I/O 的請求數(shù)降低,作業(yè)整體性能會有提升。另外,異步 I/O 是很多存儲系統(tǒng)都會采用的優(yōu)化。它使用提高 I/O 并行度的方式來解決提高作業(yè)的吞吐,是值得嘗試的下一步優(yōu)化方向。
針對這幾個問題我們進行了簡單的探索,首先是做了一個非常徹底的存算分離,直接寫入遠端存儲并且把本地磁盤直接作為 Cache 來使用,在此基礎上實踐了不同形式的 Cache。第二方面,我們實現(xiàn)了一個簡單的異步 I/O PoC,驗證其在存算分離場景上的性能提升。
3.2 直接寫入遠端與本地磁盤Cache的探索
■ 3.2.1 原始方案:基于文件的本地磁盤 Cache

直接使用遠端存儲作為主存的改動我們不作詳述,在這里主要探討 Cache 的形態(tài)與優(yōu)化問題。最簡單的架構是基于文件的 Cache 。如果遠端的文件被訪問到,它會被加載到本地磁盤 Cache。與此同時內(nèi)存 Cache 仍然存在,并且仍舊采用 BlockCache 的形式。這個形式是非常簡單高效的架構。但是內(nèi)存 BlockCache 和本地磁盤的文件 Cache 有很大的一個數(shù)據(jù)重復,這相當于浪費了很多空間。另一方面,由于文件的粒度相對較粗,對于同一個文件的不同 block ,其訪問的概率并不一樣,所以會有一些冷的 block 維持在磁盤中,降低了本地磁盤的命中率。針對這兩個問題,我們設計了全新的本地磁盤 Cache 的形態(tài),對上述問題進行優(yōu)化。
■ 3.2.2 優(yōu)化方案:基于 Block 的本地磁盤 Cache

我們提出將本地磁盤與內(nèi)存結合起來,組成一個以 block 為粒度的混合式 Cache。它使用一個整體的 LRU 進行統(tǒng)一的管理,不同 block 只有介質上的不同。內(nèi)存上相對冷的 block 會異步地刷到本地磁盤上,磁盤的 block 是按照順序以追加寫的形式來寫在底層文件中。如果由于 LRU 策略淘汰了某些磁盤的 block,必然會映射到某個文件上形成空洞。為了維持 Cache 空間有效性,我們采取了空間回收來進行優(yōu)化。空間回收的過程是一個空間和 CPU 開銷的權衡。

不同層的文件如 L0 file 、L1 file 以及 L2 file,它們的生命周期是不一樣的。對于 L0 file 來講,它的生命周期比較短一些,但是熱度相對高。對于 L2 file 來講,文件本身更容易存活,但是熱度是相對低的。根據(jù)這些不同的特點,我們可以采取不同的策略來進行空間回收。來自不同層文件 block 會被 Cache 在不同的底層文件中。針對不同的底層文件可以執(zhí)行不同的空間回收閾值與頻率,這樣可以保證最大的空間回收效率。
另外我們針對 block 淘汰策略也提出了優(yōu)化方案。最原始的 LRU 是根據(jù)命中頻率來進行管理的,某個 block 一段時間內(nèi)不命中則會被淘汰。這種策略并沒有考慮到在緩存某一個block 的空間開銷。也就是說可能為了緩存某個 block,卻有更多的 block 沒有辦法進行緩存。在這里引入了一個新的評判體系叫做緩存效率,用一段時間內(nèi)命中次數(shù)除以 block 大小,來更好的評判每一個緩存的 block 是否應該被緩存。這種評判方式的缺點是開銷會比較大。最基本的 LRU 針對于查詢都是 O(1) 的,但緩存效率的評分需要實現(xiàn)一個優(yōu)先隊列,其運行效率會有較大下降。所以在這里的思路還是在保持 LRU 主體管理的情況下,針對 block 的緩存效率異常的情況進行特殊化處理。
目前發(fā)現(xiàn)有兩部分異常,第一部分是內(nèi)存中的 data block 。它的命中率是內(nèi)存中相對低的,但是它的占比能達到 50%。目前對于它的策略就是進行壓縮,其代價是每次訪問涉及到解壓,但這個開銷要比進行一個 I/O 的開銷要小得多。第二部分是磁盤中的 filter block 。雖然它有命中,但它的大小是比較大的,緩存效率并不高。在這里實現(xiàn)了一個傾向于把磁盤中的 filter block 優(yōu)先踢出的策略,使得相對上層的數(shù)據(jù)可以緩存進來。在測試作業(yè)場景中,這兩條特殊規(guī)則與 LRU 相結合,相比于沒有這兩條規(guī)則的時候,整體 TPS 會上升 22%,效果比較顯著。

但直接寫入遠端使系統(tǒng)出現(xiàn)了遠端文件冷讀問題,即文件第一次生成后的讀取仍然需要涉及到遠端 I/O。為了解決這個問題,我們在這里也做了一個小的優(yōu)化,在本地磁盤上提供一個上傳遠端的隊列,并且讓其中的文件多緩存一段時間。這個時間并不會很長,大概是二三十秒的一個級別,在此期間隊列文件的遠端 I/O 會變?yōu)楸镜?I/O。這樣的做法能夠讓遠端冷讀的問題大大的緩解。

到目前為止我們有兩種存算分離的 Cache 方案,第一種是基于文件的本地磁盤 Cache 方案,它的優(yōu)點是非常簡單和有效,在磁盤充足的場景下有與本地方案類似的性能,因為本地磁盤可以緩存所有文件。第二種是混合式 block cache 的優(yōu)化,在本地磁盤不足的情況下是一個非常好的方案,因為它提升了 Cache 的命中率。但是它也帶來了比較大的管理開銷。如果我們想要有一個通用的方案來適配所有場景,應該怎么做呢?
■ 3.2.3 混合方案:自適應變化

將上述兩種方案結合,我們設計了一個自適應變化的的混合方案。在磁盤充足的情況下使用的是基于文件的 Cache 方案,在磁盤不足的情況下,會把本地磁盤自動的和內(nèi)存結合在一起組成混合式 block cache 方案。兩種方案的結合會讓它們兩個的優(yōu)點結合在一起,在所有的場景下都能夠最大化的滿足性能效率的需求。
■ 3.2.4 混合方案:評測


我們針對上述提出的混合方案使用測試作業(yè)進行評測??梢钥吹皆?TPS 上,新方案相比于文件為粒度的原始緩存方案有 80% 的提升。同時它也伴隨著一些 CPU 的開銷,用 CPU 效率(TPS/CPU)作為評判標準,新方案也有 40% 的提升。Cache 命中率的提升是 TPS 提升的一個主要來源。
3.3 異步I/O的探索
■ 3.3.1 同步單條處理模式

第二項探索是對 Flink 進行的異步 I/O 改造與測試。如圖展示了目前 Flink 的單線程處理模型,在 Task 線程上面,所有的數(shù)據(jù)是按順序來進行處理的。對于每一條數(shù)據(jù)處理,會分為算子(operator)的 CPU 開銷,狀態(tài)(State)訪問的 CPU 開銷,以及狀態(tài)訪問所需的 I/O 時間,其中 I/O 是最大的一塊開銷。由于存算分離需要訪問遠端存儲,其 I/O 延遲會比本地方案大,最終會導致整體 TPS 有明顯下降。
■ 3.3.2 批量處理+內(nèi)部異步模式

我們對這一模式進行更改,使得 State 操作可以同時進行。在 Task 線程的角度來講,State 被并行化之后整體的時間被縮小,所以 TPS 會有一個提升。同時,Task 線程需要預先攢批,這和 micro-batch 做的事情是非常類似的,同理也可以借用預聚合的功能,降低 state 訪問的數(shù)目,TPS 得以進一步提升。
■ 3.3.3 算子異步+批量處理模式

更進一步,在加上狀態(tài)訪問異步的基礎之上,可以繼續(xù)探索從算子的角度上進行異步化的過程。這意味著狀態(tài)訪問已經(jīng)開始了異步執(zhí)行后,讓 Task 線程得以繼續(xù)進行其他數(shù)據(jù)的 CPU 操作。但這樣做有一個問題:狀態(tài)訪問 I/O 一般都是時間比較長的,雖然在 Task 線程閑的時候可以做一些其他的數(shù)據(jù)的處理工作,但是最終會一個速率不匹配的問題,瓶頸最終還會落到狀態(tài)訪問上,會退化到?jīng)]有做此優(yōu)化的情況。
經(jīng)過權衡,我們認為僅采用攢批,再加上批內(nèi)的狀態(tài)訪問使用異步 I/O 這種方式,是一個比較平衡的方案。
■ 3.3.4 存算分離+批量化:評測

我們做了一個簡單的支持批量異步的接口的狀態(tài)后端,并在社區(qū) Microbenchmark 上面做了一個簡單的測試,目前僅涉及到 value get 的場景。從對比結果上可以看到,批量執(zhí)行加上異步 I/O 是對存算分離場景有很大的提升。
04
批量化異步 I/O 存算分離適用場景

上述探索的批量化執(zhí)行的存算分離狀態(tài)訪問有獨特的應用場景。對于大狀態(tài)作業(yè)來講,存算分離在功能上解決了最開始所述的幾個問題,在性能上,用批量接口的方式來彌補它的低的問題。
4.1 性能分析

此種方案的性能來源是 State 訪問在批次內(nèi)并行化,減少了狀態(tài)訪問的時間,提升了計算節(jié)點的 CPU 利用率。這種方案對于大狀態(tài)作業(yè)性能提升是很有用的。
4.2 定性 性能分析

在小狀態(tài)作業(yè)的場景下,狀態(tài)訪問可以做到非常的快,將狀態(tài)訪問從 Task 線程抽離出來的提升量很小,且引入了線程之間交互的開銷。所以在小狀態(tài)的場景,這種批量異步狀態(tài)訪問的方案或許還不如原始本地狀態(tài)管理方案。
隨著狀態(tài)大小逐漸增大,狀態(tài) I/O 開銷逐漸增大并成為了瓶頸,異步 I/O 的執(zhí)行當于攤薄了每個 I/O 所耗的時間。這導致了圖中紅色線的下降是較慢的,而本地狀態(tài)管理(藍色線)降低會比較快。在達到某個狀態(tài)大小后,異步 I/O 的方案性能會顯著的好。這種方案需要消耗 I/O 帶寬,如果狀態(tài)訪問已經(jīng)達到了 I/O 上限,異步 I/O 不能減少 I/O 的總時間,故此時它的斜率跟本地狀態(tài)管理差不多。

如果狀態(tài)很小的時候就達到 I/O 上限,并行化執(zhí)行并不會產(chǎn)生效果,上圖所示的便是這個場景。
總結一下,批量并異步執(zhí)行狀態(tài)訪問在滿足以下條件時會有優(yōu)勢:
-
大狀態(tài)作業(yè)場景且狀態(tài)訪問是作業(yè)的瓶頸
-
I/O 并沒有達到瓶頸(未打滿)
-
業(yè)務對于攢批的延遲(亞秒到秒級別)可以接受
絕大部分存算分離場景下,由于 I/O 性能是存儲集群提供,可以支撐比較大的 I/O 量且可以靈活伸縮,一般不會過早達到 I/O 瓶頸狀態(tài),異步 I/O 可以很好的優(yōu)化存算分離場景。
結語
以上介紹了我們在存算分離方面做的一些探索。這些工作我們希望借著 Flink 2.0 的機會貢獻給社區(qū),一方面是支持純遠端的存算分離方案+混合式緩存的存儲后端,另一方面是希望能夠引入異步化 I/O 保證存算分離模式下的高性能數(shù)據(jù)處理。
Flink Forward Asia 2023
本屆 Flink Forward Asia 更多精彩內(nèi)容,可點擊 「閱 讀原文」 或掃描圖片二維碼觀看全部議題的視頻回放及 FFA 2023 峰會資料!

▼ 關注「 Apache Flink 」,獲取更多技術干貨 ▼
