天天看點

流處理技術謬見大消除

我們在思考流處理問題上花了很多時間,更酷的是,我們也花了很多時間幫助其他人認識流處理,以及如何在他們的組織裡應用流處理來解決資料問題。

我們首先要做的是糾正人們對流處理(作為一個快速變化的領域,這裡有很多誤見值得我們思考)的錯誤認識。

在這篇文章裡,我們選出了其中的六個作為例子。因為我們對apache flink比較熟悉,是以我們會基于flink來講解這些例子。

謬見1:沒有不使用批處理的流(lambda架構) 

謬見2:延遲和吞吐量:隻能選擇一個 

謬見3:微批次意味着更好的吞吐量 

謬見4:exactly once?完全不可能 

謬見5:流隻能被應用在“實時”場景裡 

謬見6:不管怎麼樣,流仍然很複雜

謬見1:沒有不使用批處理的流(lambda架構)

“lambda架構”在apache storm的早期階段和其它流處理項目裡是一個很有用的設計模式。這個架構包含了一個“快速流層”和一個“批次層”。

流處理技術謬見大消除

之是以使用兩個單獨的層,是因為lambda架構裡的流處理隻能計算出大緻的結果(也就是說,如果中間出現了錯誤,那麼計算結果就不可信),而且隻能處理相對少量的事件。

就算storm的早期版本存在這樣的問題,但現今的很多開源流處理架構都具有容錯能力,它們可以在出現故障的前提下生成準确的計算結果,而且具有高吞吐的計算能力。是以沒有必要再為了分别得到“快”和“準确”的結果而維護多層架構。現今的流處理器(比如flink)可以同時幫你得到兩種結果。

好在人們不再更多地讨論lambda架構,說明流處理正在走向成熟。

謬見2:延遲和吞吐量:隻能選擇一個

早期的開源流處理架構要麼是“高吞吐”的,要麼是“低延遲”的,而“海量且快速”一直未能成為開源流處理架構的代名詞。

不過flink(可能還有其它的架構)就同時提供了高吞吐和低延遲。這裡有一個基準測試結果的樣例。

讓我們從底層來剖析這個例子,特别是從硬體層,并結合具有網絡瓶頸的流處理管道(很多使用flink的管道都有這個瓶頸)。在硬體層不應該存在需要作出權衡的條件,是以網絡才是影響吞吐量和延遲的主要因素。

一個設計良好的軟體系統應該會充分利用網絡的上限而不會引入瓶頸問題。不過對flink來說,總是有可優化的空間,可以讓它更接近硬體所能提供的效能。使用一個包含10個節點的叢集,flink現在每秒可以處理千萬級别的事件量,如果擴充到1000個節點,它的延遲可以降低到幾十毫秒。在我們看來,這種水準已經比很多現有的方案高出很多。

謬見3:微批次意味着更好的吞吐量

我們可以從另一個角度來讨論性能,不過先讓我們來澄清兩個容易混淆的概念:

微批次 

微批次建立在傳統批次之上,是處理資料的一個執行或程式設計模型。“通過這項技術,程序或任務可以把一個流當作一系列小型的批次或資料塊”。

緩沖 

緩沖技術用于對網絡、磁盤、緩存的通路進行優化。wikipedia完美地把它定義為“實體記憶體裡的一塊用于臨時儲存移動資料的區域“。

那麼第3個缪見就是說,使用微批次的資料處理架構能夠比每次處理一個事件的架構達到更高的吞吐量,因為微批次在網絡上傳輸的效率更高。

這個缪見忽略了一個事實,流架構不會依賴任何程式設計模型層面的批次,它們隻會在實體層面使用緩沖。

flink确實也會對資料進行緩沖,也就是說它會通過網絡發送一組處理過的記錄,而不是每次發送一條記錄。從性能方面說,不對資料進行緩沖是不可取的,因為通過網絡逐個發送記錄不會帶來任何性能上的好處。是以我們得承認在實體層面根本不存在類似一次一條記錄這樣的情況。

不過緩沖隻能作為對性能的優化,是以緩沖:

對使用者是不可見的 不應該對系統造成任何影響 不應該出現人為的邊界 不應該限制系統功能

是以對flink的使用者來說,他們開發的程式能夠單獨地處理每個記錄,那是因為flink為了提升性能隐藏了使用緩沖的細節。

事實上,在任務排程裡使用微批次會帶來額外的開銷,而如果這樣做是為了降低延遲,那麼這種開銷會隻增不減!流處理器知道該如何利用緩沖的優勢而不會帶來任務排程方面的開銷。

謬見4:exactly once?完全不可能

這個缪見包含了幾個方面的内容:

從根本上說,exactly once是不可能的 從端到端的exactly once是不可能的 exactly once從來都不是真實世界的需求 exactly once以犧牲性能為代價

讓我們退一步講,我們并不介意“exactly once”這種觀點的存在。“exactly once”原先指的是“一次性傳遞”,而現在這個詞被随意用在流處理裡,讓這個詞變得令人困惑,失去了它原本的意義。不過相關的概念還是很重要的,我們不打算跳過去。

為了盡量準确,我們把“一次性狀态”和“一次性傳遞”視為兩種不同的概念。因為之前人們對這兩個詞的使用方式導緻了它們的混淆。apache storm使用“at least once”來描述傳遞(storm不支援狀态),而apache samza使用“at least once”來描述應用狀态。

一次性狀态是指應用程式在經曆了故障以後恍如沒有發生過故障一樣。例如,假設我們在維護一個計數器應用程式,在發生了一次故障之後,它既不能多計數也不能少計數。在這裡使用“exactly once”這個詞是因為應用程式狀态認為每個消息隻被處理了一次。

一次性傳遞是指接收端(應用程式之外的系統)在故障發生後會收到處理過的事件,恍如沒有發生過故障一樣。

流處理架構在任何情況下都不保證一次性傳遞,但可以做到一次性狀态。flink可以做到一次性狀态,而且不會對性能造成顯著影響。flink還能在與flink檢查點相關的資料槽上做到一次性傳遞。

flink檢查點就是應用程式狀态的快照,flink會為應用程式定時異步地生成快照。這就是flink在發生故障時仍然能保證一次性狀态的原因:flink定時記錄(快照)輸入流的讀取位置和每個操作數的相關狀态。如果發生故障,flink會復原到之前的狀态,并重新開始計算。是以說,盡管記錄被重新處理,但從結果來看,記錄好像隻被處理過一次。

那麼端到端的一次性處理呢?通過恰當的方式讓檢查點兼具事務協調機制是可能的,換句話說,就是讓源操作和目标操作參與到檢查點裡來。在架構内部,結果是一次性的,從端到端來看,也是一次性的,或者說“接近一次性”。例如,在使用flink和kafka作為資料源并發生資料槽(hdfs)滾動時,從kafka到hdfs就是端到端的一次性處理。類似地,在把kafka作為flink的源并且把cassandra作為flink的槽時,如果針對cassandra的更新是幂等時,那麼就可以實作端到端的一次性處理。

流處理技術謬見大消除

值得一提的是,利用flink的儲存點,檢查點可以兼具狀态版本機制。使用儲存點,在保持狀态一緻性的同時還可以“随着時間移動”。這樣可以讓代碼的更新、維護、遷移、調試和各種模拟測試變得簡單。

流處理技術謬見大消除

  謬見5:流隻能被應用在“實時”場景裡

這個謬見包括幾點内容:

“我沒有低延遲的應用,是以我不需要流處理器” “流處理隻跟那些持久化之前的過渡資料有關系” “我們需要批處理器來完成笨重的離線計算”

現在是時候思考一下資料集的類型和處理模型之間的關系了。 

首先,有兩種資料集:

沒有邊界的:從非預定義的端點持續産生的資料 有邊界的:有限且完整的資料

很多真實的資料集是沒有邊界的,不管這些資料時存儲在檔案裡,還是在hdfs的目錄裡,還是在像kafka這樣的系統裡。舉一些例子:

移動裝置或網站使用者的互動資訊 實體傳感器提供的度量名額 金融市場資料 機器日志資料

實際上,在現實世界中很難找到有邊界的資料集,不過一個公司所有大樓的位置資訊倒是有邊界的(不過它也會随着公司業務的增長而變化)。

其次,有兩種處理模型:

流:隻要有資料生成就會一直處理 批次:在有限的時間内結束處理,并釋放資源

讓我們再深入一點,來區分兩種沒有邊界的資料集:連續性流和間歇性流。

流處理技術謬見大消除

使用任意一種模型來處理任意一種資料集是完全可能的,雖然這不是最優的做法。例如,批次處理模型被長時間地應用在無邊界的資料集上,特别是間歇性的無邊界資料集。現實情況是,大多數“批處理”任務是通過排程來執行的,每次隻處理無邊界資料集的一小部分。這意味着流的無邊界特質會給某些人帶來麻煩(那些工作在流入管道上的人)。

批處理是無狀态的,輸出隻取決于輸入。現實情況是,批處理任務會在内部保留狀态(比如reducer經常會保留狀态),但這些狀态隻限在批次的邊界内,而且它們不會在批次間流竄。

當有人嘗試實作類似帶有“事件時間戳”的時間窗,那麼“批次的邊界内狀态”就會變得很有用,這在處理無邊界資料集時是個很常用的手段。

處理無邊界資料集的批處理器将不可避免地遇到遲到事件(因為上遊的延遲),批次内的資料有可能是以變得不完整。要注意,這裡假設我們是基于事件時間戳來移動時間窗的,因為事件時間戳是現實當中最為準确的模型。在執行批處理的時候,遲到的資料會成為問題,即使通過簡單的時間窗修複(比如翻轉或滑動時間窗)也解決不了這個問題,特别是如果使用會話時間窗,就更難以處理了。

因為完成一個計算所需要的資料不會都在一個批次裡,是以在使用批次處理無邊界資料集時,很難保證結果的正确性。最起碼,它需要額外的開銷來處理遲到的資料,還要維護批次之間的狀态(要等到所有資料達到後才開始處理,或者重新處理批次)。

flink内建了處理遲到資料的機制,遲到資料被視為真實世界無邊界資料的正常現象,是以flink設計了一個流處理器專門處理遲到資料。

有狀态的流處理器更适合用來處理無邊界資料集,不管資料集是持續生成的還是間歇生成的。使用流處理器隻是個錦上添花的事情。

tyler akidau的系列文章“超越批處理:流101”裡有更多關于這方面内容的描述。

缪見6:不管怎麼樣,流仍然很複雜

這是最後一個缪見。你也許會想:“理論雖好,但我仍然不會采用流技術,因為……”:

流架構難以掌握 流難以解決時間窗、事件時間戳、觸發器的問題 流需要結合批次,而我已經知道如何使用批次,那為什麼還要使用流?

我們從來沒有打算慫恿你使用流,雖然我們覺得流是個很酷的東西。我們相信,是否使用流完全取決于資料和代碼的特點。

在做決定之前問問自己:“我正在跟什麼樣類型的資料集打交道?”

無邊界的(使用者活動資料、日志、傳感器資料) 有邊界的

然後再問另一個問題:“哪部分變化最頻繁?”

代碼比資料變化更頻繁 資料比代碼變化更頻繁

對于資料比代碼變化更頻繁的情況,例如在經常變化的資料集上執行一個相對固定的查詢操作,這樣會出現流方面的問題。

是以,在認定流是一個“複雜”的東西之前,你可能在不知不覺中已經解決過流方面的問題!你可能使用過基于小時的批次任務排程,團隊裡的其他人可以建立和管理這些批次(在這種情況下,你得到的結果可能是不準确的,而你意識不到這樣的結果是批次的時間問題和之前提過的狀态問題造成的)。

為了能夠提供一組封裝了這些時間和狀态複雜性的api,flink社群為此工作了很長時間。在flink裡可以很簡單地處理事件時間戳,隻要定義一個時間視窗和一個能夠抽取時間戳和水印的函數(隻在每個流上調用一次)。處理狀态也很簡單,類似于定義java變量,再把這些變量注冊到flink。使用flink的streamsql可以在源源不斷的流上面運作sql查詢。

最後一點:對代碼比資料變化更頻繁的情況該怎麼辦?對于這種情況,我們認為你遇到了探索性問題。使用筆記本或其它類似的工具進行疊代可能适合用來解決探索性問題。

在代碼穩定了之後,你仍然會碰到流方面的問題。我們建議從一開始就使用長遠的方案來解決流方面的問題。

流處理的未來

随着流處理的日漸成熟和這些缪見的逐漸淡去,我們發現流正朝着除分析應用之外的領域發展。正如我們所讨論的那樣,真實世界正連續不斷地生成資料。

傳統的做法會中斷這些連續的資料,因為這些資料必須被聚合到一個集中的位置,或者被切分成批次,友善應用程式使用。

像cqrs這樣的流處理模式越來越流行,應用程式可以直接基于持續的資料流進行開發,這樣可以在本地保留狀态,可以更好地隔離應用和團隊,可以更好地處理基于時間的資料。

随着flink不斷地演化改進,并被越來越多的企業所采用,我們相信它不僅僅能夠用來簡化分析管道,還能夠為我們帶來更強大的計算模型。

本文轉自d1net(轉載)