DataFlow編程模型與Spark Structured streaming


       流式(streaming)和批量( batch):流式數據,實際上更准確的說法應該是unbounded data(processing),也就是無邊界的連續的數據的處理;對應的批量計算,更准確的說法是bounded data(processing),亦即有明確邊界的數據的處理。 近年來流式計算框架編程接口的標准化傻瓜化SQL化日漸有走上台面的趨勢。各家計算框架都開始認真考慮相關的問題,儼然成為大家競爭的熱點方向。     

      Dataflow模型:是谷歌在處理無邊界數據的實踐中,總結的一套SDK級別的解決方案,其目標是做到在非有序的,無邊界的海量數據上,基於事件時間進行運算,並能根據數據自身的屬性進行window操作,同時數據處理過程的正確性,延遲,代價可根據需求進行靈活的調整配置。DataFlow的底層計算引擎依托於 Millwheel 實時計算框架和FlumeJava批處理框架,在谷歌開源了相關SDK以后,發起了beam項目: http://beam.incubator.apache.org/ , 為了拉攏開源社區的同學,其底層計算引擎也可以替換適配成Spark/Flink等開源計算框架。

DataFlow模型核心


       Dataflow計算模型:希望從編程模型的源頭上,統一解決傳統的流式和批量這兩種計算語意所希望處理的問題。

       和Spark通過micro batch模型來處理Streaming場景的出發點不同,Dataflow認為batch的處理模式只是streaming處理模式的一個子集。在無邊界數據集的處理過程中,要及時產出數據結果,無限等待顯然是不可能的,所以必然需要對要處理的數據划定一個窗口區間,從而對數據及時的進行分段處理和產出,而各種處理模式(stream,micro batch,session,batch),本質上,只是窗口的大小不同,窗口的划分方式不同而已

       比如,Batch的處理模式就只是一個窗口區間涵蓋了整個有邊界的數據集這樣的一種特例場景而已。一個設計良好的能處理無邊界數據集的系統,完全能在准確性和正確性上做到和“Batch”系統一樣甚至應該更好。而不是傳統的認為batch框架的正確性更好,streaming框架顧及了實時性,正確性天然就做不好,必須和batch框架配合走Lambda模型來補足(Lambda模型:用一個流式+批量的拼湊方案去解決海量無限數據的實時統計問題,雖然有各種上層封裝抽象,統一SDK編程接口方案的存在,企圖通過一套代碼,翻譯執行的方式,降低在兩套計算框架模型上開發和維護代碼的代價,但實際效果往往並不如意,翻譯執行層的存在,並不能抹平兩種計算框架在模型根源上的差異,到頭來真正能復用的代碼邏輯並不多,簡單的說就是Lambda框架本身並不解決用戶真正的痛點,而只是一種沒有出路的情況下的無奈之舉)。      

Dataflow模型里強調的兩個時間概念:Event timeProcess time

  • Event time 事件時間: 就是數據真正發生的時間,比如用戶瀏覽了一個頁面,或者下了一個訂單等等,這時候通常就會有一些數據會被生產出來,比如前者可能會產生一條用戶的瀏覽日志
  • Process time: 則是這條日志數據真正到達計算框架中被處理的時間點,簡單的說,就是你的程序是什么時候讀到這條日志的

現實情況下,由於各種原因,數據采集,傳輸到達處理系統的時間可能會有長短不同的延遲,在分布式應用場景環境下,不僅是延遲數據亂序到達往往也是常態。這些問題,在有邊界數據集的處理過程中往往並不存在,或者無關緊要。

                   

 基於這種無邊界數據集的特性,在Dataflow模型中,數據的處理過程中需要解決的問題,被概括為以下4個方面:

  • What results are being computed. : 計算邏輯是什么
  • Where in event time they are being computed. : 計算什么時候(事件時間)的數據
  • When in processing time they are materialized. : 在什么時候(處理時間)進行計算/輸出
  • How earlier results relate to later refinements. : 后續數據如何影響(修正)之前的計算結果

   清晰的定義這些問題,並針對性的在模型框架層面加以解決,正是Dataflow模型區別於其它流式計算模型的核心關鍵所在。通常的流式計算框架往往模糊或者無法有效的區別對待數據的事件時間和處理時間,對於第4個問題,如何修正數據,也可能缺乏直接的支持。這些問題通常需要開發人員在業務代碼邏輯層面,自行想辦法解決,因而也就加大了這類數據處理業務的開發難度,甚至讓這種業務的開發成為一個不可能完成的任務。Dataflow計算模型的目標是把上述4方面的問題,用明確的語意,清晰的拆分出來,更好的模塊化,從而實現在模型層面調整局部設置,就能快速適應各種業務邏輯的開發需求。Spark 2.0開始啟動的Structure Streaming API也引入了和Dataflow類似的模型思想。

理論模型:Dataflow基本上是通過構建以下三個核心功能模型來解決上面的問題:

  • 一個支持基於事件時間的窗口(window)模型,並提供簡易的API接口:支持固定窗口/滑動窗口/Session(以Key為維度,基於事件時間連續性進行划分)等窗口模式
  • 一個和數據自身特性綁定的計算結果輸出觸發模型,並提供靈活可描述的API接口
  • 一個增量更新模型,可以將數據增量更新的能力融合進上述窗口和結果觸發模型中。

窗口模型:

 

   為了在計算框架級別實現基於事件時間的窗口模型,Dataflow系統中,將常見的流式計算框架中的[key,value]兩元組tuple形式的信息數據,變換成了[key,value, event time, window ]這樣的四元組模型。

Event time的引入原因顯而易見,必須要有相關載體承載這個信息,否則只能基於Process time/Batch time 來划分窗口。而window窗口標識信息的引入,很重要的一個原因是要支持Session類型的窗口模型,而同時,要將流式和增量更新的支持融合進窗口的概念中,也勢必需要在數據中引入這樣一個顯式的窗口信息(否則,通常的做法就只能是用micro batch分組數據的方式,隱式的標識數據的窗口屬性),在消息的四元組數據結構基礎上,Dataflow通過提供對消息進行窗口賦值窗口合並按key分組按窗口分組等原子功能操作,來實現各種窗口模型。

窗口觸發模型:

    多數基於Process time定義的固定窗口或滑動窗口模型,並沒有特別強調窗口觸發這樣一個概念,因為在這類模型中,窗口的邊界時間點,也就是觸發計算結果輸出的時間點,並不需要特別加以區分。     

    對於Dataflow這樣的基於事件時間的模型來說,由於事件時間和處理時間之間存在非固定的延遲,而框架又需要正確的處理亂序的數據,這使得判斷窗口的邊界位置,進而觸發計算和結果輸出變得困難起來。在這一點上,Dataflow部分借用了底層Millwheel提供的Low watermark低水位這樣一個概念來解決窗口邊界的判斷問題,當低水位對應的時間點超過設定的時間窗口邊界時間點時,觸發窗口的計算和結果輸出。但是,低水位的概念在理論上雖然是OK的,在實際場景中,通常是一個概率模型,並不能完全保證准確的判斷事件時間的延遲情況,而且有很多場合對窗口邊界的判斷,用戶自己有自己的需求。因此,Dataflow提供了可自定義的窗口觸發模型可以使用低水位做觸發,也可以使用比如:定時觸發,計數觸發,計量觸發,模式匹配觸發或其它外部觸發源,甚至各種觸發條件的邏輯運算組合等機制來應對可能的需求。

增量更新模型:

    當一個特定時間窗口被觸發以后,后續晚到的數據如何處理,如何對之前觸發結算的結果進行修正,Dataflow在框架層面也提供了直接的支持,基本上包括三種策略:

  • 丟棄:一旦特定窗口觸發過,對應窗口的數據就丟棄,晚到的數據也丟棄
  • 累計:觸發過的窗口對應的數據保留(保留時間策略也可調整),晚到的數據更新對應窗口的輸出結果
  • 累計並更正:和累積模式類似,區別在於會先對上一次窗口觸發的結果發送一個反相修正的信息,再輸出新的結果,便於有需要的下游更正之前收到的信息

通常來說,丟棄策略實現起來最簡單,既沒有歷史數據負擔,對下游計算也不產生影響。但是前提條件是,數據亂序或者晚到的情況不嚴重或者不重要或者不影響最后的統計結果的精度。

累計策略,從窗口自身的角度來說,實現起來也不復雜,除了內存代價會高一些,因為要保留歷史窗口的數據,但是存在的問題是有些下游運算邏輯是基於上游運算結果計算的,下游計算邏輯能否正確處理重復輸出的窗口結果,正確的進行去重或者累加,往往是個問題。

累計並更正策略,就窗口自身邏輯來說,實現上會更加復雜一點,但是下游計算邏輯的編寫復雜性其實才是最難的。反相修正信息,是為了給下游提供更多的信息來解決上述窗口運算結果重復輸出問題,增加了下游鏈路去重數據的能力,但實際上,這個邏輯需要下游計算邏輯的深度配合才能實現,個人覺得,除了部分計算拓撲邏輯相對簡單的程序能夠正確處理好這種情況,依賴關系稍微復雜一點的計算鏈路,靠反相修正信息,要做到正確的累加或去重還是很困難的。

Structured Streaming


     Spark2.0新增了Structured Streaming,它是基於SparkSQL構建的可擴展和容錯的流式數據處理引擎,使得實時流式數據計算可以和離線計算采用相同的處理方式(DataFrame&SQL)。Structured Streaming顧名思義,它將數據源和計算結果都映射成一張”結構化”的表,在計算的時候以結構化的方式去操作數據流,大大方便和提高了數據開發的效率

Spark2.0之前,流式計算通過Spark Streaming進行:

 使用Spark Streaming每次只能消費當前批次內的數據,當然可以通過window操作,消費過去一段時間(多個批次)內的數據。在數據量特別大的情況下,使用window操作並不是很好的選擇,通常是借助其它如Redis、HBase等完成數據統計

 Structured Streaming將數據源和計算結果都看做是無限大的表,數據源中每個批次的數據,經過計算,都添加到結果表中作為行。

關於結算結果的輸出,有三種模式:

  • Complete Mode:輸出最新的完整的結果表數據。
  • Append Mode:只輸出結果表中本批次新增的數據,其實也就是本批次中的數據;
  • Update Mode(暫不支持):只輸出結果表中被本批次修改的數據;注意,這與完全模式不同,因為此模式不輸出未更改的行。

新增的structured streaming API,針對原先的streaming編程接口DStream的問題進行了改進,Dstream的問題包括:

  • 框架自身只能針對Batch time進行處理,很難處理event time,很難處理延遲,亂序的數據  batch interval:為Streaming應用設置的批處理間隔)
  • 流式和批量處理的API還是不完全一致,兩種使用場景中,程序代碼還是需要一定的轉換
  • 端到端的數據容錯保障邏輯需要用戶自己小心構建,增量更新和持久化存儲等一致性問題處理難度較大

這些問題其實也就是Dataflow中明確定位需要解決的問題。通過Structured Streaming API,Spark計划支持和Dataflow類似的概念,如Event time based的窗口策略,自定義的觸發邏輯,對輸出(sink)模塊的更新模式(追加,全量覆蓋,更新)的built-in支持,更加統一的處理無邊界數據和有邊界數據等。

總體看來,Spark 2.x的structured streaming 模型和Dataflow有異曲同工之處,設計的目標看起來很遠大,甚至給出了一份功能比較表格來證明其優越性

但上面的表格明顯的是有“揚長避短”的偏向性的。比如在2.1的版本中,Structured Streaming還是Alpha版的,所支持的類Dataflow模型的功能還相對簡單。2.2版本中,號稱production了,不過,應該還是從穩定性的角度來說的,功能完整性方面還有一定差距。比如還不支持session window,追加模式更新只能支持無聚合操作的場景,還有各種功能還停留在設想階段,對於join等操作還有各種各樣的限制等等,這些部分和dataflow業已實現的功能還有較大的差距。

對於exactly once發送的保障,Structured Streaming要求外部數據源具備offset定位的能力,再加上snapshot等機制來實現,而dataflow是通過對消息在框架內部進行持久化來實現replay,不依賴外部數據源的能力。

另外,prefix integrity, Transactional sink等概念,實際上是對上下游讀寫接口的一個封裝,幫用戶實現了一些業務邏輯,整體上偏外圍功能一點,用這些特性來和其它框架比較不一定客觀,因為設計理念不太不一樣。

而在Dataflow的模型設計中,用戶能更加細化的定義每個環節的步驟和設置,所以沒有把一些邏輯替用戶實現,更多的是以模塊化的方式,留給用戶去自己選擇,而Structured steaming則把很多事情包辦了,定制的余地較小,靈活性應該會差一些,不過這也給程序的自動優化帶來了一些便利

Beam


     Beam https://github.com/apache/beam  是由谷歌發起的apache 項目,基本來說就是實現dataflow編程模型的SDK項目,目標是提供一個high level的統一API編程接口,后端的執行引擎支持對接 APEX/Spark/Flink/Cloud dataflow

目前的編程語言支持Java和Python,2017年5月發布了第一個穩定版本2.0.0。

這個項目的前景如何,不太好說,單就適配各個后端的角度來說,就Spark后端來說,在spark 1.x時代,這種high level的編程模型抽象是對spark編程模型的一種add on,有一定的附加價值,但是按照spark 2.x structured streaming的發展路線來說,這一層抽象就稍微顯得有些多余了。而基於Java的語法,在表達的簡潔性上,相比scala也會帶來一些額外的代價。

Flink


   Dataflow的核心就是窗口和觸發模型,而Flink在這兩方面的實現,最接近Dataflow的理論原型,事件時間驅動,各種窗口模型,自定義觸發和亂序/晚到數據的處理等等。

Flink的Data Streaming API通過定義window方法,和window內的數據需要使用的聚合函數比如:reduce,fold,window(前兩者增量,后者全量),以及窗口觸發(Trigger)和窗口內數據的淘汰(Evictor)方法,讓用戶可以實現對Dataflow模型中定義的場景的靈活處置,比如:需要在大數據量,大窗口尺度內實現實時連續輸出結果的目的。通過allow late數據的時間范圍來處理晚到數據。對於延遲數據會觸發聚合結果的再次輸出,這個和Dataflow的模型不同的是,Flink本身是不提供反向信息輸出的,需要業務邏輯自行做必要的去重處理。對於Flink的實現,對數據的聚合和淘汰方式,給用戶留下了足夠靈活的選擇,畢竟在工程實踐中,長時間,大窗口,連續結果輸出這種場景很常見,比如實時統計一天之類各個小時段的PV/UV,5秒更新一次結果。這種情況下,要避免OOM,還要正確處理晚到數據,追數據等問題,預聚合和提前觸發的能力就必不可少了。

至於SQL化這條路,Flink的SQL語法解析和優化是依賴Apache Calcite實現的,而Calcite對window語法的支持才剛剛開始,所以FlinkSQL目前還不支持Streaming模型

整體感覺Flink目前在Dataflow模型思想方面實現的成熟度比Spark Structured Streaming要好

StreamCQL


     華為的StreamCQL方案,是構建在Storm之上的,簡單的說就是提供了一個流式SQL的編程接口,執行時,底層翻譯成Storm的拓撲邏輯提交執行。整體上,StreamCQL做的好的地方是,SQL的支持比較完整,其它框架,在Stream這個場景,SQL的支持,或多或少還在開發完善中。

     StreamCQL最大的問題,是它的編程模型,和Dataflow的模型還有很大的差距。      

     整體上來說,StreamCQL的框架邏輯,就是使用窗口來buffer一部分數據,然后當窗口結束條件滿足時,釋放出這批數據給下游觸發一次計算流程。粗看和Dataflow沒有太大的區別,但實際上,最主要的差距,是StreamCQL對窗口模型的定義,其次是觸發和數據更新模型的缺失。    

     StreamCQL的窗口模型,支持Batch(也就是固定間隔窗口)和Slide,但是窗口的划分默認是基於處理時間Process Time的!!!而且,雖然窗口內的數據可以再細分Partition,但窗口只有一個。。。不能同時處理幾個窗口,意味着無法處理數據亂序或者晚到的情況。而Slide窗口的定義,也和主流的Slide窗口定義不同,每次對下游更新離開窗口范圍的數據,看起來更像一個FIFO Queue的實現。

     盡管可以使用Trigger關鍵字,將Batch窗口的觸發條件改為消息中的某一個字段或者表達式,從而通過指定事件時間字段,近似的達到基於事件時間的窗口划分。但是,實際上,因為單一的窗口機制,這樣做,也只能處理事件源嚴格遞增的場景。而現實情況中,來自不同客戶端的事件,時間必然是亂序的,實時流計算的來源也主要是分布式消息隊列(如kafka),進一步導致全局的無序,所以現實中,基本是不可能存在消息中事件時間嚴格遞增的場景。

      此外,由於缺乏靈活的數據更新和淘汰方式的定義,StreamCQL的主流程基本上是Buffer一堆數據,然后計算加淘汰這批數據,所以,缺乏數據預聚合的能力,這就導致窗口范圍內所有的數據在窗口關閉之前,都必須保存在內存中。因此即使是事件時間嚴格遞增或者只關心Process Time的場景,Window的范圍也不能太大,否則很容易超過內存限制,造成OOM,而實際上,多數場景,只需要保留增量聚合后的結果數據就足夠了。

總體來說,StreamCQL的SQL語法比較完善,但計算模型在理論和架構實現方面存在較大的不足,所以如果不加改造,在實際工程應用中很難有大的做為。

 

參考學習資料:


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM