溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何理解Flink實(shí)時(shí)應(yīng)用的確定性

發(fā)布時(shí)間:2021-11-23 16:19:37 來源:億速云 閱讀:112 作者:柒染 欄目:大數(shù)據(jù)

這篇文章給大家介紹如何理解Flink實(shí)時(shí)應(yīng)用的確定性,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

確定性(Determinism)是計(jì)算機(jī)科學(xué)中十分重要的特性,確定性的算法保證對于給定相同的輸入總是產(chǎn)生相同的輸出。在分布式實(shí)時(shí)計(jì)算領(lǐng)域,確定性是業(yè)界一直難以解決的課題,由此導(dǎo)致用離線計(jì)算修正實(shí)時(shí)計(jì)算結(jié)果的 Lambda 架構(gòu)成為大數(shù)據(jù)領(lǐng)域過去近十年的主流架構(gòu)。

而在最近幾年隨著 Google The Dataflow Model 的提出,實(shí)時(shí)計(jì)算和離線計(jì)算的關(guān)系逐漸清晰,在實(shí)時(shí)計(jì)算中提供與離線計(jì)算一致的確定性成為可能。本文將基于流行實(shí)時(shí)計(jì)算引擎 Apache Flink,梳理構(gòu)建一個(gè)確定性的實(shí)時(shí)應(yīng)用要滿足什么條件。

確定性與準(zhǔn)確性

比起確定性,準(zhǔn)確性(Accuracy)可能是我們接觸更多的近義詞,大多數(shù)場景下兩者可以混用,但其實(shí)它們稍有不同: 準(zhǔn)確的東西一定是確定的,但確定性的東西未必百分百準(zhǔn)確。在大數(shù)據(jù)領(lǐng)域,不少算法可以根據(jù)需求調(diào)整成本和準(zhǔn)確性的平衡,比如 HyperLogLog 去重統(tǒng)計(jì)算法給出的結(jié)果是有一定誤差的(因此不是準(zhǔn)確的),但卻同時(shí)是確定性的(重算可以得到相同結(jié)果)。

要分區(qū)確定性和準(zhǔn)確性的緣故是,準(zhǔn)確性與具體的業(yè)務(wù)邏輯緊密耦合難以評估,而確定性則是通用的需求(除去少數(shù)場景用戶故意使用非確定性的算法)。當(dāng)一個(gè) Flink 實(shí)時(shí)應(yīng)用提供確定性,意味著它在異常場景的自動重試或者手動重流數(shù)據(jù)的情況下,都能像離線作業(yè)一般產(chǎn)出相同的結(jié)果,這將很大程度上提高用戶的信任度。

影響 Flink 應(yīng)用確定性的因素

投遞語義


常見的投遞語義有 At-Most-Once、At-Least-Once 和 Exactly-Once 三種。嚴(yán)格來說只有 Exactly-Once滿足確定性的要求,但如果整個(gè)業(yè)務(wù)邏輯是冪等的, 基于At-Least-Once 也可以達(dá)到結(jié)果的確定性。

實(shí)時(shí)計(jì)算的 Exactly-Once 通常指端到端的 Exactly-Once,保證輸出到下游系統(tǒng)的數(shù)據(jù)和上游的數(shù)據(jù)是一致的,沒有重復(fù)計(jì)算或者數(shù)據(jù)丟失。要達(dá)到這點(diǎn),需要分別實(shí)現(xiàn)讀取數(shù)據(jù)源(Source 端)的 Exactly-Once、計(jì)算的 Exactly-Once 和輸出到下游系統(tǒng)(Sink 端)的 Exactly-Once。

其中前面兩個(gè)都比較好保證,因?yàn)?Flink 應(yīng)用出現(xiàn)異常會自動恢復(fù)至最近一個(gè)成功 checkpoint,Pull-Based 的 Source 的狀態(tài)和 Flink 內(nèi)部計(jì)算的狀態(tài)都會自動回滾到快照時(shí)間點(diǎn),而問題在于 Push-Based 的 Sink 端。Sink 端是否能順利回滾依賴于外部系統(tǒng)的特性,通常來說需要外部系統(tǒng)支持事務(wù),然而不少大數(shù)據(jù)組件對事務(wù)的支持并不是很好,即使是實(shí)時(shí)計(jì)算最常用的 Kafka 也直到 2017 年的 0.11 版本才支持事務(wù),更多的組件需要依賴各種 trick 來達(dá)到某種場景下的 Exactly-Once。

總體來說這些 trick 可以分為兩大類:

  • 依賴寫操作的冪等性。比如 HBase 等 KV 存儲雖然沒有提供跨行事務(wù),但可以通過冪等寫操作配合基于主鍵的 Upsert 操作達(dá)到 Exactly-Once。不過由于 Upsert 不能表達(dá) Delete 操作,這種模式不適合有 Delete 的業(yè)務(wù)場景。
  • 預(yù)寫日志(WAL,Write-Ahead-Log)。預(yù)寫日志是廣泛應(yīng)用于事物機(jī)制的技術(shù),包括 MySQL、PostgreSQL 等成熟關(guān)系型數(shù)據(jù)庫的事物都基于預(yù)寫日志。預(yù)寫日志的基本原理先將變更寫入緩存區(qū),等事務(wù)提交的時(shí)候再一次全部應(yīng)用。比如 HDFS/S3 等文件系統(tǒng)本身并不提供事務(wù),因此實(shí)現(xiàn)預(yù)寫日志的重?fù)?dān)落到了它們的用戶(比如 Flink)身上。通過先寫臨時(shí)的文件/對象,等 Flink Checkpoint 成功后再提交,F(xiàn)link 的 FileSystem Connector 實(shí)現(xiàn)了 Exactly-Once。然而,預(yù)寫日志只能保證事務(wù)的原子性和持久性,不能保證一致性和隔離性。為此 FileSystem Connector 通過將預(yù)寫日志設(shè)為隱藏文件的方式提供了隔離性,至于一致性(比如臨時(shí)文件的清理)則無法保證。

為了保證 Flink 應(yīng)用的確定性,在選用官方 Connector,特別是 Sink Connector 時(shí),用戶應(yīng)該留意官方文檔關(guān)于 Connector 投遞語義的說明[3]。此外,在實(shí)現(xiàn)定制化的 Sink Connector 時(shí)也需要明確達(dá)到何種投遞語義,可以參考利用外部系統(tǒng)的事務(wù)、寫操作的冪等性或預(yù)寫日志三種方式達(dá)到 Exactly-Once 語義。

函數(shù)副作用

函數(shù)副作用是指用戶函數(shù)對外界造成了計(jì)算框架意料之外的影響。比如典型的是在一個(gè) Map 函數(shù)里將中間結(jié)果寫到數(shù)據(jù)庫,如果 Flink 作業(yè)異常自動重啟,那么數(shù)據(jù)可能被寫兩遍,導(dǎo)致不確定性。對于這種情況,F(xiàn)link 提供了基于 Checkpoint 的兩階段提交的鉤子(CheckpointedFunction 和 CheckpointListener),用戶可以用它來實(shí)現(xiàn)事務(wù),以消除副作用的不確定性。另外還有一種常見的情況是,用戶使用本地文件來保存臨時(shí)數(shù)據(jù),這些數(shù)據(jù)在 Task 重新調(diào)度的時(shí)候很可能丟失。其他的場景或許還有很多,總而言之,如果需要在用戶函數(shù)里改變外部系統(tǒng)的狀態(tài),請確保 Flink 對這些操作是知情的(比如用 State API 記錄狀態(tài),設(shè)置 Checkpoint 鉤子)。

Processing Time

在算法中引入當(dāng)前時(shí)間作為參數(shù)是常見的操作,但在實(shí)時(shí)計(jì)算中引入當(dāng)前系統(tǒng)時(shí)間,即 Processing Time,是造成不確定性的最常見也最難避免的原因。對 Processing 的引用可以是很明顯、有完善文檔標(biāo)注的,比如 Flink 的 Time Characteristic,但也可能是完全出乎用戶意料的,比如來源于緩存等常用的技術(shù)。為此,筆者總結(jié)了幾類常見的 Processing Time 引用:

  • Flink 提供的 Time Characteristic。Time Characteristic 會影響所有使用與時(shí)間相關(guān)的算子,比如 Processing Time 會讓窗口聚合使用當(dāng)前系統(tǒng)時(shí)間來分配窗口和觸發(fā)計(jì)算,造成不確定性。另外,Processing Timer 也有類似的影響。
  • 直接在函數(shù)里訪問外部存儲。因?yàn)檫@種訪問是基于外部存儲某個(gè) Processing Time 時(shí)間點(diǎn)的狀態(tài),這個(gè)狀態(tài)很可能在下次訪問時(shí)就發(fā)生了變化,導(dǎo)致不確定性。要獲得確定性的結(jié)果,比起簡單查詢外部存儲的某個(gè)時(shí)間點(diǎn)的狀態(tài),我們應(yīng)該獲取它狀態(tài)變更的歷史,然后根據(jù)當(dāng)前 Event Time 去查詢對應(yīng)的狀態(tài)。這也是 Flink SQL 中 Temporary Table Join 的實(shí)現(xiàn)原理[1]。
  • 對外部數(shù)據(jù)的緩存。在計(jì)算流量很大的數(shù)據(jù)時(shí),很多情況下用戶會選擇用緩存來減輕外部存儲的負(fù)載,但這可能會造成查詢結(jié)果的不一致,而且這種不一致是不確定的。無論是使用超時(shí)閾值、LRU(Least Recently Used)等直接和系統(tǒng)時(shí)間相關(guān)的緩存剔除策略,還是 FIFO(First In First Out)、LFU(Less Frequently Used)等沒有直接關(guān)聯(lián)時(shí)間的剔除策略,訪問緩存得到的結(jié)果通常和消息的到達(dá)順序相關(guān),而在上游經(jīng)過 shuffle 的算子里面這是難以保證的(沒有 shuffle 的 Embarrassingly Parallel 作業(yè)是例外)。
  • Flink 的 StateTTL。StateTTL 是 Flink 內(nèi)置的根據(jù)時(shí)間自動清理 State 的機(jī)制,而這里的時(shí)間目前只提供 Processing Time,無論 Flink 本身使用的是 Processing Time 還是 Event Time 作為 Time Characteristic。BTW,StateTTL 對 Event Time 的支持可以關(guān)注 FLINK-12005[2]。

綜合來講,要完全避免 Processing Time 造成的影響是非常困難的,不過輕微的不確定性對于業(yè)務(wù)來說通常是可以接受的,我們要做的更多是提前預(yù)料到可能的影響,保證不確定性在可控范圍內(nèi)。

Watermark

Watermark 作為計(jì)算 Event Time 的機(jī)制,其中一個(gè)很重要的用途是決定實(shí)時(shí)計(jì)算何時(shí)要輸出計(jì)算結(jié)果,類似文件結(jié)束標(biāo)志符(EOF)在離線批計(jì)算中達(dá)到的效果。然而,在輸出結(jié)果之后可能還會有遲到的數(shù)據(jù)到達(dá),這稱為窗口完整性問題(Window Completeness)。

窗口完整性問題無法避免,應(yīng)對辦法是要么更新計(jì)算結(jié)果,要么丟棄這部分?jǐn)?shù)據(jù)。因?yàn)殡x線場景延遲容忍度較大,離線作業(yè)可以推遲一定時(shí)間開始,盡可能地將延遲數(shù)據(jù)納入計(jì)算。而實(shí)時(shí)場景對延遲有比較高的要求,因此一般是輸出結(jié)果后讓狀態(tài)保存一段時(shí)間,在這段時(shí)間內(nèi)根據(jù)遲到數(shù)據(jù)持續(xù)更新結(jié)果(即 Allowed Lateness),此后將數(shù)據(jù)丟棄。因?yàn)槎ㄎ唬瑢?shí)時(shí)計(jì)算天然可能出現(xiàn)更多被丟棄的遲到數(shù)據(jù),這將和 Watermark 的生成算法緊密相關(guān)。

雖然 Watermark 的生成是流式的,但 Watermark 的下發(fā)是斷點(diǎn)式的。Flink 的 Watermark 下發(fā)策略有 Periodic 和 Punctuated 兩種,前者基于 Processing Time 定時(shí)觸發(fā),后者根據(jù)數(shù)據(jù)流中的特殊消息觸發(fā)。

如何理解Flink實(shí)時(shí)應(yīng)用的確定性
圖1. Periodic Watermark 正常狀態(tài)與重放追數(shù)據(jù)狀態(tài)  

基于 Processing Time 的 Periodic Watermark 具有不確定。在平時(shí)流量平穩(wěn)的時(shí)候 Watermark 的提升可能是階梯式的(見圖1(a));然而在重放歷史數(shù)據(jù)的情況下,相同長度的系統(tǒng)時(shí)間內(nèi)處理的數(shù)據(jù)量可能會大很多(見圖1(b)),并且伴有 Event Time 傾斜(即有的 Source 的 Event Time 明顯比其他要快或慢,導(dǎo)致取最小值的總體 Watermark 被慢 Watermark 拖慢),導(dǎo)致本來丟棄的遲到數(shù)據(jù),現(xiàn)在變?yōu)?Allowed Lateness 之內(nèi)的數(shù)據(jù)(見圖1中紅色元素)。

如何理解Flink實(shí)時(shí)應(yīng)用的確定性  
圖2. Punctuated Watermark 正常狀態(tài)與重放追數(shù)據(jù)狀態(tài)  

相比之下 Punctuated Watermark 更為穩(wěn)定,無論在正常情況(見圖2(a))還是在重放數(shù)據(jù)的情況(見圖2(b))下,下發(fā)的 Watermark 都是一致的,不過依然有 Event Time 傾斜的風(fēng)險(xiǎn)。對于這點(diǎn),F(xiàn)link 社區(qū)起草了 FLIP-27 來處理[4]?;驹硎?Source 節(jié)點(diǎn)會選擇性地消費(fèi)或阻塞某個(gè) partition/shard,讓總體的 Event Time 保持接近。

除了 Watermark 的下發(fā)有不確定之外,還有個(gè)問題是現(xiàn)在 Watermark 并沒有被納入 Checkpoint 快照中。這意味著在作業(yè)從 Checkpoint 恢復(fù)之后,Watermark 會重新開始算,導(dǎo)致 Watermark 的不確定。這個(gè)問題在 FLINK-5601[5] 有記錄,但目前只體現(xiàn)了 Window 算子的 Watermark,而在 StateTTL 支持 Event Time 后,或許每個(gè)算子都要記錄自己的 Watermark。

綜上所述,Watermark 目前是很難做到非常確定的,但因?yàn)?Watermark 的不確定性是通過丟棄遲到數(shù)據(jù)導(dǎo)致計(jì)算結(jié)果的不確定性的,只要沒有丟棄遲到數(shù)據(jù),無論中間 Watermark 的變化如何,最終的結(jié)果都是相同的。

確定性不足是阻礙實(shí)時(shí)計(jì)算在關(guān)鍵業(yè)務(wù)應(yīng)用的主要因素,不過當(dāng)前業(yè)界已經(jīng)具備了解決問題的理論基礎(chǔ),剩下的更多是計(jì)算框架后續(xù)迭代和工程實(shí)踐上的問題。就目前開發(fā) Flink 實(shí)時(shí)應(yīng)用而言,需要注意投遞語義、函數(shù)副作用、Processing Time 和 Watermark 這幾點(diǎn)造成的不確定性。

關(guān)于如何理解Flink實(shí)時(shí)應(yīng)用的確定性就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI