本文譯自谷歌大神 Tyler Akidau 關於流式計算的兩篇博客的第一篇,寫於2015年8月,後來又寫過一本書《Streaming Systems》,同樣值得一讀。
流數據處理是當今大數據世界的一大難題,理由如下:
- 業務希望獲取更加及時的數據,因而切換到流式處理是降低延遲的好辦法。
- 為海量又無限的數據而設計的系統,更能應對這種日益增長的業務數據的需求。
- 在數據到達時即處理數據,能夠使得工作負載更加均勻,從而產生更一致和可預測的資源消耗
儘管業務驅動導致流計算的興趣日益增加,但是和批處理相比,現有的大多數流式系統仍然不夠成熟,這也導致了最近該領域的很多有意思的發展。
作為一個最近5年多一直在谷歌的大規模流式系統(MillWheel, Cloud Dataflow)工作的我來說,很樂於看到這種源源不斷的流計算的思潮。對於如何保證人們理解並應用流式系統,特別是在現存的批處理和流處理系統存在一定的語義鴻溝的情況下,我很感興趣。因而接下來的內容將分為兩個部分:
- Streaming 101:第一篇文章會介紹基本的背景情況以及一些術語,然後再深入到時域,以及對批處理和流處理的一般方法做高度的概括。
- Dataflow 模型:第二篇文章主要介紹 Cloud Dataflow 所使用的批流統一的模型,並以一個例子應用不同的場景來輔助說明。最後對現有的批處理和流處理系統做一個簡要的語義上的比較。
閑話少說,下面進入主題。
背景
開始前,我會介紹一些重要的背景信息, 以幫助構建接下來我將要討論的主題框架。下面我會分為三個部分:
- 術語:越是討論複雜的話題,就越是需要精準的定義。對於當下有各種解釋的術語,我會具體說明在這裡的含義。
- 功能:我會指出流式系統的缺點,同時提出構建數據處理系統的框架思想,以滿足消費者日益增長的需求。
- 時域:我會介紹數據處理中兩種主要的時域、它們的相關性,並指出二者帶來的困難。
術語: 流(streaming)是什麼?
在接下來的內容之前,首先我們要解決的是:流到底是什麼?「流」可以表示各種不同的意思,這可能導致我們誤解流到底是什麼以及流式系統究竟能做什麼。鑒於此,我會儘可能地準確定義這些術語。
當前問題的關鍵在於許多事情應該由它們是什麼(例如無限數據處理、近似結果等)來定義,然而事實上經常由它們在歷史上是如何(例如通過流計算引擎)實現的來進行介紹。術語上的這種不準確導致了流的真正含義變得模糊,有時候甚至把流式系統的功能局限於流的特性,如近似和推測結果。由於精心設計的流式系統也能夠和批處理系統一樣產生正確、一致、可重複的結果。所以這裡我傾向給「流」下一個更加精確的定義:一種針對無限數據集而設計的數據處理引擎。僅此而已。(為了防止有失偏頗,有必要說明一下這個定義同時包括了真正的流以及微批的實現方式。)
作為「流」的常見場景,下面是我常聽到的,每個都被精確定義,我也建議我們的社區應該採用:
- 無限數據:一種不斷增長的,本質上無限的數據集。這些通常被稱為「流數據」(streaming data)。然而,當應用於數據集時,流或批的術語是有問題的,因為這意味著使用特定的執行引擎來處理這些數據集。兩種類型的數據集之間的關鍵區別在於現實中它們的有限性,因此最好用這種能夠區分它們特徵的術語。因此,我傾向於將無限的「流」數據集稱為無限數據,有限的「批」數據集叫做有限數據。
- 無限數據處理:一種持續的數據處理模式,適用於上述的無限數據。雖然我個人很想使用「流」這個術語來描述數據處理的類型,但是這種情況下的使用再次暗示使用了流計算引擎,這根本就是誤導;而自從設計了批處理系統以來,我們就一直重複運行批處理引擎來處理無限數據(相反地,精心設計的流式系統能夠比批處理系統更會處理有限數據)。因此,為了清楚定義,我將簡單地稱之為無限數據處理。
- 低延遲、近似、推測結果:結果的類型往往和流計算引擎相關。事實上,批處理系統從一開始就沒有被設計為低延遲或者推測結果,這是歷史事實,僅此而已。當然,如果有必要的話批處理引擎完全有能力產生近似結果。因此,對於上述的術語,應該按照它們本來的樣子(低延遲、近似、推測結果)來描述,這勝過說它們歷史上如何表現的(通過流計算引擎)。
從現在開始,任何時候使用術語「流」,意思都是設計用於無限數據集的執行引擎,僅此而已。當我談到上述任何其他術語時,我會直接說無限數據,無限數據處理或低延遲、近似、推測結果。這些是我們在 Cloud Dataflow 中採用的術語,同時我鼓勵其他人也如此使用。
被過分誇大的流處理的局限
接下來,談一談流式處理系統能做什麼和不能做什麼,重點在能做什麼;其中我最想做的事情就是討論一個精心設計的流處理究竟可以做到什麼。流式系統長期以來一直被放在提供低延遲,不準確/推測結果的場景里,通常結合批處理系統來提供最終正確的結果,即 Lambda 架構。
對於不熟悉 Lambda 架構的你來說,只需要知道它的基本思想就是在批處理系統旁邊運行一個流處理系統,並且執行基本相同的計算。流式處理系統提供低延遲、不準確的結果(由於使用近似演算法,或者因為流系統本身不提供準確性的保證),所以每過一段時間,批處理系統便持續滾動處理並計算出正確的結果。該思想最初是由 Twitter 的 Nathan Marz(Apache Storm 的創始人)提出的,最終是相當成功的因為在當時這是個很棒的想法;流計算引擎在正確性方面讓人失望,而批處理天生笨拙,所以 Lambda 為您提供了一種方法讓您魚和熊掌兼得。然而維護 Lambda 系統是一件麻煩:需要構建和維護兩個版本的管道,並且要將兩者的結果合併。
和一些常年從事於強一致性流計算引擎的人一樣,我對 Lambda 架構的整個概念感到有點討厭。不出所料,當 Jay Kreps 的文章 Lambda 架構質疑 一出,我變成了他的鐵粉。這是反對雙模式執行的必要性的首次有力陳述。Kreps 使用 Kafka 這樣的可重放系統作為流計算架構的內部連接,從而解決了可重複的問題,甚至進一步提出了 Kappa 架構,這基本意味著可以使用一個精心設計的系統來運行單一的管道。我不確定這個概念需要個名字,但是我完全支持這個概念。
老實說,我會走得更遠。我會討論一個精心設計的流式系統事實上提供了一個批處理功能之上的嚴格超集。感謝 Flink 的開發者將這一思想牢記於心,並構建了一個一直完全流式的系統,甚至包含批的模式。
所有這一切的必然結果是廣泛成熟的流式系統,加上用於無限數據處理的健壯框架,最終 Lambda 架構將回到它所屬的大數據的歷史洪荒中去。我相信這將成為現實。因為在這場比賽中打敗批處理,你今需要兩個概念:
- 正確性——這讓你與批處理平起平坐
首先,正確性歸結為一致性存儲。流式系統需要伴隨時間存在的檢查點來持久化狀態信息(有些內容 Kreps 在他的 為什麼本地狀態是是流處理的基本原語 有提及),而且必須設計得足夠好,能夠在機器出現故障時保持一致性。當 Spark Streaming 幾年前首次出現在公開的大數據場景下時,那簡直是幽暗的流計算世界裡一致性的燈塔。慶幸的是自那以後情況有所好轉,但是仍然有不少流式系統運行在沒有強一致性的情況下;我真的不敢相信至多一次處理仍然是個問題,但事實就是。
重申一下,因為這點很重要:僅僅一次的處理要求很強的一致性,這對於正確性是必要條件,而且這對於希望達到甚至超過批處理系統的能力的任何系統而言都是必需的。除非你真的不在乎結果,否則我懇求你不要使用那些不能提供強一致性狀態的流式系統。批處理系統不需要你事先驗證它們是否能夠產生正確的結果;因而不要把時間浪費在那些不能實現相同目標的流式系統上。如果想了解更多關於流式系統中如何實現強一致性,可以參考 MillWheel 和 Spark Streaming 論文。兩篇論文花了大量時間討論一致性。由於這些論文對此給出了高質量的介紹,本文將不會贅述。
- 關於時間的推理工具——這讓你超越批處理對於亂序無限數據流,數據產生的時間和數據真正被處理的時間之間的的偏差很大,用於推理時間的工具至關重要。越來越多的現代數據集體現了這個特點,現有的批處理系統(以及大多數流處理系統)缺乏必要的工具來應對這個問題。接下來以及下一篇文章,我們都將聚焦於此。首先,我們將對時域概念有一個基本的了解,之後我們將更深入了解我所說的無限的、亂序的、不同的事件時間傾斜是什麼意思。剩下的時間我們再了解使用批處理和流式系統處理有限和無限的數據的常用方法。
事件時間 vs. 處理時間
要闡述無限數據的處理方式,需要清楚地了解時間所涉及的領域。在任何數據處理系統中,通常有兩種時間值得關註:
- 事件時間,即事件發生的真實時間
- 處理時間,即事件被系統觀察到的時間
並不是所有情況下都需要關心事件的時間(如果你的不需要,萬歲啊——你的生活因此更加簡單),但大多數情況下需要,例如根據時間刻畫用戶行為,大多數計費應用程序、不同類型的異常檢測。
在一個理想的世界中,事件在發生時即被處理,因而事件時間和處理時間總是相等的。然而,現實並非如此,事件時間和處理時間之間總會存在偏差,而且通常嚴重受到數據底層輸入源,執行引擎甚至硬體的影響。可能影響的因素包括:
- 共享資源限制,如網路擁塞,網路分區或在非專用環境中共享CPU。
- 軟體因素,如分散式系統的複雜邏輯、資源競爭等。
- 數據本身的特性,包括 key 的分布、吞吐的差異、亂序(例如將飛機上的所有乘客的手機都從飛行模式中退出來)。
因此,如果要將實際系統中事件時間和處理時間的進度關係畫出來的話,您得到的應該是類似於圖1中紅線的結果。