超越批處理的世界:流計算101

OReillyData發表於2016-05-10

編者注:本文是關於資料處理演進的兩部分系列博文的第一篇。這個系列主要關注流計算系統、無窮資料集和大資料的未來。


今日,流式資料處理是大資料裡的很重要一環。原因有不少,其中包括:


  • 商業(競爭)極度渴望更快的資料,而轉換成流計算則是一個好的方法來降低延遲。

  • 海量的、無窮資料集在現在的商業環境裡變的越來越常見,而用專門設計來處理這樣資料的系統來應對這些資料則更為容易。

  • 在資料到達時就對他們進行處理能夠更加平均地把負載進行均衡,取得更好的一致性和更可預測的計算資源消耗。


儘管業務驅動帶來了對流計算興趣的猛增,但絕大部分現有的流計算系統相比於批處理還不夠成熟,而後者已經產生了很多令人激動的、多產的應用。


作為從事海量大規模流計算系統的從業者(在谷歌工作超過五年,開發了MillWheel和Cloud Dataflow),我很高興能看到對於流計算的時代熱潮。考慮到批處理系統和流計算系統在語義上的不同,我也很願意來幫助大家來理解流計算的方方面面,如它能做什麼?怎麼使用它最好?O’Reilly的編輯邀請我就我在2015 Strata+Hadoop World倫敦大會上的演講《對批處理說再見》寫一些文字的東西。這就是你所看到的這篇博文。因為需要覆蓋的題目很多,我把他們分成了兩篇來寫:

  1. 流計算101:本文是第一篇,主要介紹一些基礎背景知識,澄清了一些技術術語。隨後會進入技術細節,關注時間域的內容,並對常見的資料處理方法(包括批處理和流計算)做一個高層次的總覽。

  2. 資料流的模型:第二篇文章將會主要介紹Cloud Dataflow所使用的統一的批處理加流計算的模型,以及此模式應用於多種資料集場景下的一個具體案例。之後,我會就現有的批處理和流計算系統做一個簡單的語義比較作為整篇的結論。


好的,下面會有很長的內容,讓我們變成技術狂吧。


背景


開始我會介紹一些對我們理解後文的內容很重要的背景知識。我會分三個主題來講:

  • 技術術語:為了能精確地講解複雜的題目,必須對相關術語做精確的定義。對於一些已經被濫用的術語,我也會很明確地說明我用它們時的意思。

  • 能力:我會對一些反覆感受到的流計算系統的缺點做一些評論。我也會提出我所認為的資料處理系統的建造者應採用的基本思路,基於這樣思路構建的系統或可以應對現代資料消費者不斷增長的需求。

  • 時間域:我會介紹與資料處理相關的兩個主要時間域概念,解釋他們是如何相關的,並給出這兩個域所帶來的一些難題。


技術術語:什麼是流計算

在繼續前行前,讓我們先解決一個重要問題:“什麼是流計算?”。儘管文章到這裡為止我也是在隨意的用著這個名詞。流計算這個詞有很多不同的意思,這就導致了關於到底什麼是流計算或者到底流計算系統能做什麼的誤解。正因如此,我願意在這裡先精確地定義它。


這個問題的難點在於很多術語本應該被描述成他們是什麼(例如無窮資料處理和近似結果處理),但卻被描述為他們過去是怎麼被實現的(例如通過流計算執行引擎)。缺乏精確的定義模糊了流計算真正的意思,在某些場合下它還被貼上了它的能力僅限於“流”的那些特徵(如近似結果、推測結果處理)的標籤。鑑於良好設計的流計算系統能與現有的批處理引擎一樣產生準確、一致和可再現的結果,我更願意把流計算非常明確地定義為:一種被設計來處理無窮資料集的資料處理系統引擎。僅此而已。考慮到完整性,需要強調的是這個定義不僅包含了真正的流計算實現,也包括微批處理(micro-batch)的實現。

下面是與流計算相關的其他幾個經常出現的術語,我也給出了更精確和清晰的解釋。希望業界能夠採納和使用。


無窮資料(Unbounded data:一種持續生成,本質上是無窮盡的資料集。它經常會被稱為“流資料”。然而,用流和批次來定義資料集的時候就有問題了,因為如前所述,這就意味著用處理資料的引擎的型別來定義資料的型別。現實中,這兩類資料的本質區別在於是否有限,因此用能體現出這個區別的詞彙來定性資料就更好一些。因此我更傾向於用無窮資料來指代無限流資料集,用有窮資料來指代有限的批次資料。


無窮資料處理(Unbounded data processing:一種發展中的資料處理模式,應用於前面所說的無窮資料型別。儘管我本人也喜歡使用流式計算來代表這種型別的資料處理方式,但是在本文這個環境裡,這個說法是誤導的。用批處理引擎迴圈執行來處理無窮資料這個方法在批處理系統剛開始構思的時候就出現了。相反的,設計完善的流計算系統則比批處理系統更能承擔處理有窮資料的工作。因此,為了清晰明瞭,本文裡我就只用無窮資料處理。


低延遲,近似和/或推測性結果(Low-latencyapproximateand/or speculative results:這些結果和流處理引擎經常關聯在一起。批處理系統傳統上不是設計來處理低延遲或推測性結果這個事實僅僅是一個歷史產物,並無它意。當然,如果想,批處理引擎也完全能產生近似結果。因此就如其他的術語,最好是用這些術語是什麼來描述這些結果,而不是用歷史上它們是用什麼東西(通過流計算引擎)產生的來描述。


此後,文裡任何地方我使用術語“流計算”,我就是指為無窮資料集所設計的處理引擎,僅此而已。當我使用上述任何術語時,我就會明確說無窮資料、無窮資料處理,或低延遲,近似和/或推測性結果。這些也是我在Cloud Dataflow裡使用的術語,我也建議業界去使用。


流計算的最誇張的限制

下面讓我們看看流計算系統能和不能做什麼,重點是能做什麼。在這個博文裡我非常想讓讀者瞭解的一件事便是一個設計合理的流計算系統能做什麼。長久以來,流計算系統被認為是專為提供低延遲、不精確/推測性結果的某些特定市場而設計,並配合一個更強大的批處理系統來提供最終準確的結果,如Lambda架構(Lambda Architecture)。


對於不熟悉Lambda架構的讀者,它的基本思想就是與批處理系統一起執行流計算系統,同時進行幾乎一樣的計算。流計算系統提供低延遲、不準確的結果(或是因為使用了近似演算法,或是因為流計算系統本身沒能提供足夠準確的結果),而一段時間之後當批處理計算完成,再給出正確的結果。這個架構最初是由推特的內森•馬茲(Natan Marz,Storm的發明人)提出的,結果在當時非常成功。因為在當時這是一個非常好的主意:流計算引擎在正確性方面還令人失望,而批處理引擎則是固有的緩慢和笨重,所以Lambda就給出了一套現成的解決方案。不幸的是,維護Lambda系統是一個麻煩:需要搭建、部署、維護兩套獨立的資料流管道系統,並將兩個系統產生的結果在最後進行某種程度的合併。


作為曾多年從事強一致流計算引擎的從業者,我認為Lambda架構的基本原理是有問題的。不出意外,我是傑伊•克雷普(Jay Krep)的博文《質問Lambda架構》的超級粉絲。很高興的,下面是反對雙模式執行必要性的很好的陳述之一。克雷普通過使用可重放的系統(如Kafka)作為流計算交匯點來解決重複性的問題,並更進一步的提出“Kafka架構”。此架構的基本思路就是使用單套合理設計的引擎作為資料流管道來處理Lambda關注的任務。雖然我並不認同這個概念需要一個名字,但是我完全支援這個觀點裡的基本原理。


實話實說,我願意更進一步。我認為設計良好的流計算系統的能力是批處理系統的功能的超集(包含關係)。或許排除增量的效益,未來將不再需要如今日的批處理系統1。Flink基於這個想法開發了一套完全流計算模式的系統(同時也支援批處理模式)的做法是值得稱讚的。我喜歡他們的工作!


上述思路的必然結果就是,結合了魯棒的框架、並不斷成熟的流計算系統可以充分應對無窮資料,也終將會把Lambda架構送進博物館。我認為這個時刻已經到來。因為如果想用流計算在批處理擅長的領域打敗它,你只需要能實現兩件事:


1. 正確性:這保證流計算能和批處理平起平坐。

本質上,準確性取決於儲存的一致性。流計算系統需要一些類似於checkpoint的方法來保證長時間的持久化狀態。克雷普斯(Kreps)在他的博文《為什麼本地狀態化是流計算系統的一個基礎》討論了這個問題。同時流計算系統還必須針對系統當機後還能保證資料一致性進行精心的設計。幾年前,當Spark剛剛出現在大資料領域的時候,它幾乎就是照亮了流計算黑暗面的燈塔(譯者注:因為Spark支援強一致)。在這之後,情況越來越好。但是還是有不少流計算系統被設計和開發成儘量不去支援強一致性。我實在是不能明白為什麼“最多處理一次(at-most-once processing)”這樣的方式仍然存在。

再次強調一遍重點:強一致性必須是“只處理一次(exactly-once processing)”,這樣才能保證正確性。只有這樣的系統才能追平並最終超越批處理系統。除非你對計算的結果是否正確並不介意,否則我還是請你放棄任何不能保證強一致性的流計算系統。現有的批處理系統都保證強一致性,不會讓你在使用前去檢查計算結果是否正確。所以也不要浪費你的時間在那些達不到這樣標準的流計算系統上。

如果你很想了解如何才能在一個流計算系統裡提供強一致性,我建議你去讀一讀MillWheel和Spark Streaming這兩個連結裡的文章。兩篇文章都有相當的篇幅來介紹一致性。同時這個題目也有大量的文獻可供參考,所以這裡就不再詳細討論了。


2. 時間推理的工具:這一點讓流計算超越批處理。

在處理無窮的、無序的、事件—時間分佈不均衡的資料時,好的時間推理工具對於流計算系統是極其重要的。現在越來越多的資料已經呈現出上面的這些特徵,而現有的批處理系統(也包括幾乎所有的流計算系統)都缺少必要的工具來應對這些特性帶來的難題。我會在這篇文章的餘下部分和下一篇博文的大部分內容裡來關注於這個題目。

首先,我會介紹時間域裡的一些重要概念。隨後我會深入介紹上面所說的無窮性、無序性和事件—時間分佈不均衡這幾個特性。在本文剩下的部分裡面,我會介紹常見的處理無窮和有窮資料的方法,包括批處理和流計算兩種系統。


事件時間和處理時間

為了能更好的說明無窮資料處理,就需要很非常清楚的理解時間域的內容。任何一個資料處理系統裡,都包含兩種典型的時間:

  • 事件時間(Event time):是指事件發生的時間。

  • 處理時間(Processing time):系統觀察到事件發生的時間。

不是所有的應用場景都關心事件時間(如果你的場景不用,你的日子就好過多了),但大部分都關注。隨便舉幾個例子,比如一段時間裡的使用者行為刻畫、計費應用和很多的異常檢測應用。

理想化的情況下,事件時間和處理時間應該總是相同的,即事件在它發生的同時就被處理了。但現實是殘酷的,處理時間和事件時間之間的偏移不僅是非零的,還經常是由多種因素(如輸入源、處理引擎和硬體)的特性所共同組合成的一個可變方程。可以影響這個偏移的因素包括:

  • 共享的資源使用情況:比如網路擁塞、網路分割槽或共享環境裡的CPU使用情況。

  • 軟體因素:如步分佈系統邏輯、資源爭奪等。

  • 資料自身的特徵:包括鍵分佈、吞吐量變化、失序導致的變化(比如乘坐飛機的旅客在飛機落地後把手機從飛行模型調整到正常模式,然後某些事件才發生)。

因此,如果把在實際系統裡的事件時間和處理時間的關係畫出來,你很可能會得到類似圖1這樣的一些圖。

 640?wx_fmt=jpeg


圖1:時間域對應的例子。圖裡X軸代表系統裡的事件時間,即事件發生的時間在某一點之前的所有事件,Y軸代表事件被處理的時間,即處理某事件資料時系統的時間。泰勒•阿克道製作


圖中,黑色的虛線的斜率是1,代表了理想的情況,即事件時間和處理時間是一樣的。紅色的線代表現實的情況。在這個例子裡,系統在處理時間開始階段有一些延遲,隨後趨於理想狀況的同步,最後又產生了一些延遲。在理想情況和實際情況之間的水平距離則代表了處理時間和事件時間之間的偏移。本質上,偏移就是由處理管道產生的延遲。

可見事件時間和處理時間之間的偏移並不是靜態的,這就意味著如果你關注的是事件時間(比如事件確切發生的時間點),在你處理資料資料時不能只看資料被觀察的時間(處理時間)。不幸的是,現在很多的流計算系統卻是按照處理時間設計來處理無窮資料的。為了應對無窮資料集的無限的特性,這些系統一般都會提供一些把輸入資料按時間分片的機制。下面會仔細的討論分片機制,但其本質都是按時間把資料切割成有限的塊。

如果你真正關心的是正確性並希望分析的是事件時間,你就不能用處理時間來定義資料的時間邊界(比如,用處理時間來分片),雖然現有的很多流計算系統是這麼做的。鑑於事件時間和系統時間之間沒有一個一致的關聯,某些資料可能會被錯誤的分到按處理時間分片的資料片裡,儘管它們的事件時間並不屬於這個片。這可能是由於分散式系統內在的延遲,或是由於很多資料來源的線上/離線特性所造成的。但後果就是準確性就無法得到保證。下面(包括下一篇博文)我會用一些案例來更詳細地討論這個問題。

糟糕的是,即便是用事件時間來分片,情況也不那麼美好。對於無窮資料,失序和偏移的變化給分片帶來了另外一個問題:完整性,即既然無法預測事件時間和處理時間之間的偏移,你怎麼能確定你獲得了分片時間內的所有資料?對很多的真實資料,這個問題的答案是無法確定。現有的大部分資料處理系統都依賴某種完整性的想法,對於無窮資料而言這可能會帶來嚴重的困難。

我建議與其試圖去把無窮的資料梳理成有限的資訊片,我們應該設計這樣的工具(系統),他們可以讓我生活在這些複雜資料造成的不確定性中。當新的資料到來時,我們可以抽取或者更新舊資料。任何系統都應該能應對這些不確定性,去方便的優化完整性的概念,而不只是一個口頭上的必須。

在介紹我們是如何在Cloud Dataflow裡面使用Dataflow模型去構建這樣一個系統前,讓我們再講一些有用的背景知識:常見的資料處理模式。


資料處理模式

到目前為止,我們已經獲得了足夠的背景知識來開始研究處理無窮資料和有窮資料的常見的核心模型。下面我會在批處理和流計算兩種引擎的環境下分別對兩種處理模式進行介紹。這裡我把微批處理和流計算歸為一種,因為在這個層面上,他們沒有什麼特別大的區別。


有窮資料處理

處理有窮資料是很簡單直接的,相信大家都比較的熟悉了。如下圖(圖2)所示,我們會先對左邊非結構化的據進行操作。使用某種分析引擎(通常是批處理型別的,但一個設計良好的流計算引擎也能做的一樣好),比如MapReduce,對這些資料做運算。最後得到圖右邊所示的有規則的結構化資料,並獲得其內在的價值。

640?wx_fmt=jpeg

圖2:用經典的批處理引擎來處理有窮資料。左邊有限的非結構化資料經過一個資料處理引擎的處理,轉變成了右側的相應的結構化資料。泰勒•阿克道製作

 

儘管上述過程可能有無數多種變形的版本,但他們總體的模式是很簡單的。更有趣的是如何處理無窮資料集,下面就讓我們來看一看各種處理無窮資料的典型方法。我們從使用傳統的批處理引擎開始,最後以使用專門為無窮資料集而設計的系統(例如大部分流計算或微批處理系統)來結束。


無窮資料批處理

批處理引擎儘管不是設計來處理無窮資料的,但從它們誕生開始就已經被用來處理無窮資料集了。可以想像的是,這個方法一般都涉及到把無窮資料分片成一系列有窮資料集,再用批處理引擎來處理。


固定的時間視窗

批處理引擎最常見的處理無窮資料集的方法就是重複性地把輸入資料按固定時間視窗分片,然後再把每個片當作一個獨立有窮資料來源進行處理。特別是像日誌這樣的資料來源,事件被記錄進有層級的檔案系統,而日誌檔案的名字就對應於它們相應的時間視窗。第一感就會用這個(固定視窗)方法。因為本質上,在資料建立之前就已經進行了基於事件時間的排列來把資料寫入適當的時間視窗了。

然而在實際場景中,很多系統依然需要處理完整性的問題。例如,要是由於網路原因某些事件寫入日誌被延遲了,怎麼辦?要是你的所有日誌都要被傳送到一個通用的儲存區後才能被處理,怎麼辦?要是事件是從移動裝置上傳送來的,怎麼辦?這些場景都會需要對原先的處理方法進行一定的修改(例如,延遲處理知道確保所有的時間片內的事件都收集齊;或如果有資料晚到了,就對整個時間視窗內的資料再處理一次)。

 640?wx_fmt=jpeg

圖3:通過臨時的固定視窗,用經典的批處理引擎來處理無窮資料。無窮資料集先通過固定的時間視窗被採集整理成有窮資料,然後再通過重複執行批處理引擎來處理。泰勒•阿克道製作


會話單元

更復雜的時間視窗策略可以是會話單元。這個方法把無窮資料進行了更細的劃分,以方便批處理引擎來處理無窮資料。會話一般是被定義為活動(如某個特定使用者)的時間週期,以一段時間的不活躍來判定結束。使用批處理引擎來計算會話單元時,也經常會碰到同一個會話被分到了兩個單元裡,就如圖4裡的紅色塊所示。這種情況是可以通過增加批次的大小來減少,但相應的延遲也就會增加。另外一個選擇是增加額外的邏輯來把分到前一次執行裡的會話補進本次運算,但這會帶來額外的複雜度。

640?wx_fmt=jpeg

圖4:通過臨時的固定視窗並結合會話單元,用經典的批處理引擎來處理無窮資料。無窮資料集先通過固定的時間視窗被採集整理成有窮資料,並再進一步劃分成不同的會話單元,然後再通過重複執行批處理引擎來處理。泰勒•阿克道製作


使用傳統的批處理引擎來計算會話單元還不是最理想的方法。一個更好的方法則是用流的方式來構建會話。下面我們就來討論。


無窮資料流計算

與基於批次的無窮資料處理方法的臨時特性相反的是,流計算系統天生就是為無窮資料開發的。如前文所說的,對於很多現實世界裡的分散式資料來源,你不僅要應對資料的無窮性,還要處理下面兩個特性:

  • 對應於事件時間的高度無序性。這意味著你需要某種程度上對事件做時間排序,如果你想按事件時間來做分析。

  • 時間漂移的變化性。這意味著在一個固定的Y時間的增量裡你不能假定你可以看到大部分發生在對應的X時間增量範圍內的事件。

有多種可以處理有這樣特性的資料集的方法。我大致把它們分成四類:

  • 時間不可知(Time-agnostic)

  • 近似演算法(Approximation algorithms)

  • 按處理時間做時間視窗分片

  • 按事件時間做時間視窗分片


下面就分別看一看這幾種方法。


時間不可知

時間不可知處理方法的使用場景是當時間本質上無關,所有的邏輯僅關心資料本身而非時間。因為這種場景下只關心資料的到達,所以並不需要流計算引擎來做特殊的支援,只要保證資料的傳遞就可以了。因此,本質上現有的所有流計算系統都支援時間不可知場景(當然對於準確性有要求的使用者,還需要排除那些對強一致性的保證不支援的系統)。批處理系統也能很好的支援時間不可知的無窮資料的應用場景,只要簡單地把資料分成特定的有窮資料塊序列,再依次處理即可。下面會介紹一些實際的例子,但考慮到這種場景的處理比較的容易理解,我不會用過多的篇幅。

過濾(Filtering

非常基礎的一個場景就是過濾。比如你要處理網站流量日誌,想過濾掉不是來自某個特定域名的所有流量。那麼就只要在資料到達的時候,檢查一下是不是來自那個特定的域,如果不是就丟棄掉。這種場景只依賴於資料元素本身,因此跟資料來源是否是無窮的、失序的或是延遲有變化的就沒有關係了。

640?wx_fmt=jpeg

圖5:過濾無窮資料。不同型別的資料從圖左向右流進,被過濾後形成了只包含一種型別資料的統一資料集。泰勒•阿克道製作。


內連線(Inner-Join

另外一種時間不可知的應用場景就是內連線(也叫雜湊連線, Hash-Join)。當連線兩個無窮資料來源的時候,如果你只想得到在兩個資料來源裡都有的元素,那麼這裡的邏輯就跟時間無關了。在得到一個新的值後,只要簡單地把它持久的快取起來,再一直等到另外一個源裡也送來這個值,然後輸出即可。當然有可能這裡會需要一些垃圾回收機制來把那些從來沒出現的連線的元素給清理掉,這時候就跟時間有關了。但是對於那些不會出現不完全連線的場景,這個就沒什麼了。

640?wx_fmt=jpeg

圖6:對兩個無窮資料來源做內連線。當來自兩個資料來源中都出現了相同的觀察值後,就進行連線操作。泰勒•阿克道製作。


如果問題轉變成了外連線(Outer-Join),這就會出現之前討論的完整性的問題,即當你看到連線的一邊,你怎麼能知道另外一邊是否會出現?事實是,你不知道!這種情況下,你就必須採用某種超時機制,而這就又涉及到了時間。這裡的時間本質上就又是一種時間視窗分片,後面會仔細分析。


近似演算法(Approximation algorithms

640?wx_fmt=jpeg


圖7:對無窮資料來源進行近似計算。資料進入後通過了一個複雜的近似演算法的運算,得到差不多你想要的結果。泰勒•阿克道製作。

第二類方法是近似演算法,比如近似Top-N、流K-means聚類等。他們都以無窮資料為輸入,並計算出差不多你想要的結果。這些近似演算法的好處是它們一般開銷小,而且就是設計來處理無窮資料的。缺點是這類方法數量有限,且實現都比較複雜,更新也難。近似的特性又使得它們不能廣泛應用。

值得注意的是,這些演算法一般都有一些時間域的特性(例如,某種衰退機制)。同時也因為這些方法一般都是在資料到達後就處理,所以它們基本用的都是處理時間。對於有些可以提供證明的錯誤範圍的演算法,這一點很重要。因為如果演算法能夠利用資料到達的順序來預測錯誤範圍,那麼即便是事件—時間漂移有變化,對於無窮資料,這些錯誤都可以忽略不計了。請記住這一點。

近似演算法本身是很有趣的話題,但它們本質上也是時間不可知方法的一種(如果不考慮它們自身帶有的一些時間域特性)。而且這些演算法也很直白容易理解和使用,這裡就不再詳細地介紹了。


時間視窗分片

另外兩個無窮資料處理的方法都是時間視窗分片法的變形。在繼續前,我會花一些篇幅來講清楚時間視窗分片的具體含義是什麼。分片就只是對應於一個輸入資料來源(無窮或有窮),按時間區間把資料分成有限個片,再來處理。圖8裡面給出了三種分片的方式。

 640?wx_fmt=jpeg

圖8:不同的時間視窗分片機制。每個例子都包括三個輸入鍵對應的資料,並按不同的分片方式進行了劃分,如視窗對齊的(對所有的鍵都適用)和視窗不對齊的(只對應於某些鍵的)。泰勒•阿克道製作。


  • 固定視窗(Fixed windows):固定時間視窗按固定長度的時間來分片。如圖8所示,固定時間視窗典型地會對所有的資料集進行劃分,也叫對齊的視窗。在某些情形下,可能會希望對不同的資料子集應用不同的相位偏移,從而能讓分片的完整度更加的平均。這時就不再是對齊的視窗,而是非對齊的。

  • 滑動視窗(Sliding windows):滑動視窗是固定視窗的一個更一般化的形式。一般會定義兩個量,即視窗大小(時間長短)和滑動時間。如果滑動時間比視窗要小,則視窗會重疊;如果相等,這就是固定視窗;如果滑動時間比視窗大,就產生了一種特殊的資料取樣,也就是按時間只看資料集裡的一部分子集的資料。類似於固定視窗,滑動視窗一般也是對齊的。出於效能考慮也會在某些情況下是非對齊的。需要注意的是,圖8裡為了能表明滑動的性質而沒有把每個視窗對應到所有的鍵。實際情況裡是都要對應到的。

  • 會話單元(Sessions):是動態視窗的一種。一個會話是在不活躍時間段之間的一連串事件。這個不活躍時間一般是設定的比超時的時間要長。會話單元一般用來做使用者行為分析,即觀察在一個會話單元裡使用者的一系列事件。會話單元的長度一般都沒法提前確定,完全取決於實際資料的情況。會話單元也是非對齊視窗的一個經典案例,因為實際情況下,不同子集資料的會話單元長度幾乎不可能一致地對齊。

上面討論的處理時間和事件時間是我們最關心的兩個概念2。在兩種情況下,時間視窗分片都可以使用。所以下面我們會詳細的來看看他們的區別。由於按處理時間做視窗分片是最常見的,我們就想講它吧。

按處理時間做時間視窗分片

640?wx_fmt=jpeg

圖9:按處理時間對資料做固定視窗的分片。資料按照它們到達處理管道的時間(處理時間)順序地被分成固定視窗的片。泰勒•阿克道製作。


這種方式下,系統本質上是把進來的資料進行快取,達到一定的處理時間視窗再對快取的資料進行處理。例如,在一個5分鐘的固定視窗裡,系統會按自己的系統時間快取5分鐘內的資料,然後把這5分鐘內的資料視為一片,交由流程的下一步做處理。

用處理時間做視窗分片有一下幾個好的特性:

  • 簡單。實現起來非常簡單明瞭,不用擔心資料失序和重排序。只要把資料快取後按時交給下游就好了。

  • 判斷完整性很容易。因為系統能很清楚地知道某視窗裡的資料是否已經全部到到,所以資料的完整性很容易保證。這就意味著系統不用操心去處理那些“晚到的”資料了。

  • 如果你關心的是事件被觀察到後的資訊,那麼按處理時間做時間視窗分片就是你所需要的方法。很多監控應用場景都可以歸到這一類。比如你想獲得某大型網站的每秒訪問量,再通過監控這個數量來判斷網站是否有服務中斷,這時候用處理時間做時間視窗分片就是絕佳的選擇。

儘管有這些好處,這個方法也有一個非常大的缺陷,即如果要處理的資料包含事件時間,而時間視窗需要反映的是資料的事件時間,那麼就需要資料嚴格地按照事件時間來到達。不幸的是,在現實中這種按事件時間排好序到達的資料幾乎是沒有的。

舉一個簡單的例子,手機裡的App收集上傳使用者的使用資料用於後期分析。當手機離網一段時間後(比如無網路連線、飛航模式等),這期間記錄的資料就需要等到手機接入網路後才能上傳。這意味著處理時間和事件時間就會出現從分鐘到幾周不等的偏移。這時候用處理時間來做時間視窗分片就沒法對這樣的資料做出有效的處理併產生有用的資訊。

另外一個例子是有些分散式的資料來源在系統正常情況下可以提供按事件時間排序好(甚至非常好)的資料。但是當系統的健康狀況得不到保證的時候,就很難保證有序性了。比如某全球業務需要處理採集自多個大洲的資料。而洲際間的網路頻寬一般會受限(不幸的是,這很常見),這時就會出現突然間一部分資料會比通常情況下晚到。再用按處理時間做分片,就不再能有效地反映資料實際發生時的情景了。這時視窗內的資料就已經是新舊混合的資料了。

這兩個例子裡,我們真正想用的都是事件的發生時間,因為這樣才能保證資料到達的有序性。這就需要按事件時間進行時間視窗分片。

按事件時間做時間視窗分片

當你需要的是把事件按照發生時的時間分進有限的塊內,你所需要的就是按事件時間做時間視窗分片。這是時間視窗分片的黃金標準。很不幸,目前絕大多數系統都不支援這樣的方法。儘管那些支援強一致的系統(比如Hadoop和Spark)經過一些修改都可以支援這種方法。

下圖就給出了一個用一小時的固定視窗對無窮資料做按事件時間分片的演示。640?wx_fmt=jpeg

圖10:按照事件時間用固定視窗分片。資料按照他們發生的時間收集。白色箭頭指出把那些事件時間屬於同一個分片的資料放到同一個視窗中去。泰勒•阿克道製作。


圖裡的白色箭頭線對應於兩個特別的資料。這兩個資料先後達到處理管道的時間和他們的事件時間並不一致。如果是按照處理時間來分片處理,但實際我們關心的是事件發生時的資訊,那麼計算出的結果就會不正確了。如此,用事件時間分片來保證事件時間計算的正確性就很完美了。

這個方法來處理無窮資料的另外一個好處就是你可以使用動態大小視窗,比如會話單元,而不用出現前面用批處理引擎來處理會話時會出現的會話被分到兩個視窗裡(見圖4)。

640?wx_fmt=jpeg


圖11:按照事件時間做會話單元的視窗分片。資料按照他們發生的時間以及活動性被分到了不同的會話單元裡。白色箭頭指出把那些事件時間屬於同一個分片的資料放到同一個視窗中去並按事件時間排序。泰勒•阿克道製作。


當然,天下沒有免費的午餐,按事件時間做時間視窗分片也不例外。由於視窗必須要比視窗的長度存在更長的時間(處理時間),所以它有兩個很大的缺點。

  • 快取:由於視窗的存在時間要長,所以就需要快取更多的資料。比較好的是,現在持久化已經是整個資料處理系統資源裡最便宜的部分(其他的是CPU、頻寬和記憶體)。所以在一個設計良好的資料處理系統裡,用強一致的持久化機制加上好的記憶體快取機制後,這個問題可能並沒想像的那麼嚴重。另外不少的聚合運算(比如求和、算平均)都不需要把所有的資料都快取起來,只要把很小的中間結果快取下來,並逐步累積就可以了。

  • 完整性:考慮到我們通常沒有好的方法來確定已經收集到了一個視窗片裡的所有資料,我們怎麼知道什麼時候可以把視窗裡的資料交給下游去處理?事實是,我們確實不知道。對很多輸入型別,系統可以給出一個相對合理準確的完整性估計,比如在MillWheel系統裡使用的水印(在第二篇博文裡會有更多的介紹)。但是對於絕對的準確要求極度高的場景(比如計費),唯一的選擇就是提供一個方法讓引擎來決定什麼時候交出資料,同時能讓系統不斷地修正結果。應對視窗內資料的完整性是一個非常有趣的題目,但最好是能在一個具體的例子裡來討論說明,以後我會再介紹。


結論

哇噢!很多內容是不是?如果你堅持讀到這裡,你應該得到表揚。到這裡我基本講了我想說的一半內容。所以讓我們適當地的停一下,在繼續第二篇文章前,先回顧一下都講了什麼。令人高興的是,第一篇的內容比較的沉悶,而第二篇的內容會相對有趣很多。


回顧

在上文中,我們已經:

  • 澄清了一些術語,特別是把流計算的內涵侷限到了執行引擎。同時把流計算這一大概念下的一些術語都用了描述性的詞彙來做概念上的區別,如無窮資料、近似結果/推測結果等。

  • 評估了設計良好的批處理和流計算系統的相對能力,並提出流計算系統的能力是批處理系統的超集。而類似於Lambda架構這樣的主張(認為流計算比批處理要差一些)終會被成熟的流計算系統所取代。

  • 提出了對於流計算系統而言兩個重要的概念,實現這兩個概念就可以幫助流計算系統追趕並最終超越批處理系統。他們分別是正確性和時間推理工具。

  • 介紹了事件時間和處理時間的重要區別,指出了在分析資料何時發生的情況下,這些區別所帶來的困難。並提出了處理方法需要從主張完整性轉變到適應資料隨時間變化的特性。

  • 分析了目前批處理系統和流計算系統對於有窮和無窮資料的主要的資料處理方法。並大致把處理無窮資料的方法分為四類:時間不可知型、近似演算法、按處理時間做時間視窗分片和按事件時間做時間視窗分片。


下一篇

本篇為第二篇博文所要的具體的案例提供了必須的上下文。在第二篇博文裡,我會介紹如下的主要內容。

  • 從概念上來介紹我們是如何在Dataflow模型裡把資料處理的過程按四個維度進行劃分:什麼(what)、哪裡(where)、什麼時候(when)以及如何做(how)。

  • 用一個簡單具體的資料集來詳細的介紹多個場景下的處理方法。重點強調使用Dataflow模型和各種API能處理的案例。通過這些例子我們可以更好的理解上面所說的事件時間和處理時間。同時也會介紹一個新的概念—水印(watermark)。

  • 針對博文裡所介紹的重點特徵來比較現有的資料處理系統,從而能幫助大家更好的做選擇。同時也鼓勵大家對系統裡有缺陷的地方進行改進,從而能最終實現我的目標,即在大資料產業中完善現有的資料處理系統,特別是流計算系統。


好,就到這裡,下一篇見!


1. 我這裡提出效益增量並不是流計算系統內在的缺陷,而只是很多的流計算系統在設計時的決策的產物。批處理系統相對於流計算系統的效益增量更多的是因為更好的打包機制和更有效的排序傳輸機制。現在的批處理系統開發了很多非常好的優化機制,從而能用非常便宜的硬體資源來處理海量的吞吐。沒理由認為我們不能把批處理系統裡這些好的機制運用到為無窮資料設計的系統裡。這樣使用者就可以在高延遲、高效率的批處理系統與低延遲、低效率的流計算系統間做出靈活的選擇了。我們在Cloud Dataflow裡面就是這麼做的,即使用統一的模型同時提供批處理和流計算的兩個執行環境。我們使用兩個執行環境主要是因為我們恰好有兩套獨立的系統分別為他們特殊的應用做了很好的優化。從工程的角度長遠來看,我喜歡把這兩個系統裡的精華融合成單一的系統,同時還能保留不同的效率層次以便讓使用者來靈活選擇。不過現在我們還沒有做。實話實說由於使用了統一的Dataflow模型,可能也沒有必要這麼做。所以這種情況可能不會發生了。

2. 如果你檢視足夠多的學術論文或基於SQL的流計算系統,你可能還會發現第三種時間視窗分片的機制:基於記錄的視窗分片,例如視窗的大小取決於記錄裡元素的多少。然而,基於記錄的視窗分片本質上也是一種基於處理時間的時間視窗分片。就是元素按照他們到達系統的時間來遞增的賦予時間戳。有鑑於此,我就不會在這裡討論這種方式了(在第二篇文章裡會有一個關於這種方式的例子)。


作者:泰勒•阿克道(Tyler Akidau)


泰勒•阿克道是谷歌的一名高階軟體工程師。他是谷歌內部流計算資料處理系統(如MillWheel)的技術帶頭人,在過去的五年裡開發了多個大規模流計算資料處理系統。他熱忱地認為流計算應該是大規模海量計算的更通用的模型。他最喜歡的交通工具是貨運自行車,可以把他的兩個小女兒放到拖兜裡面。 


640?wx_fmt=jpeg



閱讀原文(read more) 獲得更多資訊。

相關文章