流式統計的幾個難點

在本文發出之後不久,老外就寫了一篇類似內容的。人家比我寫得好,推薦大家讀這篇
http://radar.oreilly.com/2015/08/the-world-beyond-batch-streaming-101….

流式統計聽着挺容易的一個事情,說到底不就是數數嘛,每個告警系統裏基本上都有一個簡單的流式統計模塊。但是當時基於storm做的時候,這幾個問題還是困擾了我很長時間的。沒有用過spark streaming/flink,不知道下面這些問題在spark streaming/flink裏是不是都已經解決得很好了。

時間窗口切分問題

做流式統計首要的問題是把一個時間窗口內的數據統計到一起。問題是,什麼是時間窗口?有兩種選擇

  • 日誌時間(event timestamp)
  • 牆上時間(wall clock)

最簡單的時間窗口統計的是基於“牆上時間”的,每過1分鐘就切分出一個新窗口出來。比如statsd,它的窗口切分就是這樣的。這種基於“牆上時間”的統計有一個非常嚴重的問題是不能回放數據流。當數據流是實時產生的時候,“牆上時間”的一分鐘也就只會有一分鐘的event被產生出來。但是如果統計的數據流是基於歷史event的,那麼一分鐘可以產生消費的event數量只受限於數據處理速度。另外event在分佈式採集的時候也遇到有快有慢的問題,一分鐘內產生的event未必可以在一分鐘內精確到達統計端,這樣就會因爲採集的延遲波動影響統計數據的準確性。實際上基於“牆上時間”統計需要

collection latency = wall clock - event timestamp

基於“牆上時間”的統計需要採集延遲非常小,波動也很小纔可以工作良好。大部分時候更現實的選擇是需要基於“日誌時間”來進行窗口統計的。

使用“日誌時間”就會引入數據亂序的問題,對於一個實時event stream流,其每個event的timestamp未必是嚴格遞增的。這種亂序有兩種因素引入:

  • event產生的機器的時鐘不完全同步(NTP有100ms左右的不同步)
  • event從採集到到達kafka的速度不均衡(不同的網絡線路有快有慢)

我們希望的流式統計是這樣的:

但是實際上數據只是基本有序的,也就是在時間窗口的邊緣會有一些event需要跨到另外一個窗口去:

最簡單的分發event到時間窗口代碼是這樣的

window index = event timestamp / window size

對1分鐘的時間窗口 window size 就是60,timestamp除以60爲相同window index的event就是在同一個時間窗口的。問題的關鍵是,什麼時候我可以確信這個時間窗口內的event都已經到齊了。如果到齊了,就可以開始統計出這個時間窗口內的指標了。然後突然又有一個落後於大夥的event落到這個已經被計算過的時間窗口如何處理?

  • 對於大部分統計而言,一個時間窗口統計出多條結果存入db並不是什麼大的問題,從db裏查詢的時候把多條結果再合併就可以了。
  • 對於一些類型的統計(非monad),比如平均值,時間窗口內的event分爲兩批統計出來的結果是沒有辦法被再次彙總的。
  • 實時類的計算對時間敏感,來晚了的數據就沒有意義了。比如告警,一個時間窗過去了就沒有必要再理會這個時間窗口了。

所以對於來晚了的數據就兩種策略:要麼再統計一條結果出來,要麼直接丟棄。要確定什麼時候一個時間窗口內的event已經到齊了,有幾種策略:

  • sleep 等待一段時間(牆上時間)
  • event timestamp超過了時間窗口一點點不關閉當前時間窗口,而是要等event timestamp大幅超出時間窗口的時候才關閉窗口。比如12:05:30秒的event到了才關閉12:04:00 ~ 12:05:00的時間窗口。
  • 一兩個event超出了時間窗口不關閉,只有當“大量”的event超出時間窗口才關閉。比如1個event超過12:05分不關閉,如果有100個event超過了12:05的時間窗口就關閉它。

三種策略其實都是“等”,只是等的依據不同。實踐中,第二種策略也就是根據“日誌時間”的等待是最容易實現的。如果對於過期的event不是丟棄,而是要再次統計一條結果出來,那麼過期的窗口要重新打開,又要經過一輪“等待”去判斷這個過去的窗口什麼時候再被關閉。

在spark上已經有人做類似的嘗試了:Building Big Data Operational Intelligence platform with Apache Spark - Eric Carr (Guavus)

多流合併的問題

一個kafka的partition就是一個流,一個kafka topic的多個partition就是多個獨立的流(offset彼此獨立增長)。多個kafka topic顯然是多個獨立的流。流式統計經常需要把多個流合併統計到一起。這種裏會遇到兩個難題

  • 多個流的速度不一樣,如何判斷一個時間窗口內的event都到齊了。如果按照前面的等待策略,可能處理一個流內部的基本有序局部亂序是有效的,但是對於多個流速差異很大的流就無能爲力了。一個很快的流很容易把時間窗口往後推得很遠,把其他流遠遠跑到後面。
  • 流速不均不能靠下游兜着,下游的內存是有限的。根本上是需要一種“背壓”的機制,讓下游通知流速過快的上游,你慢點產生新的event,等等其他人。

舉一個具體的例子:

發佈了25 篇原創文章 · 獲贊 19 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章