設計數據密集型應用(10-11):大數據的批處理和流處理

第 10 章和第 11 章,介紹「大數據」的處理,分兩個大類:

  1. 批處理(Batch Processing),用於處理大規模離線任務。「大規模」體現在:每次處理輸入的數據量大;每次處理運行的時間長(可能幾分鐘~幾天)。
  2. 流處理(Stream Processing),用於處理半離線、准實時任務。流處理系統每次處理的數據一般是一個剛剛生成的「數據」/「事件(event)」。

大數據處理,主要要解決三個問題:

  1. 數據挖掘。
  2. 擴展性。
  3. 容錯性。

批處理系統和流處理系統主要解決 2 和 3 兩個問題。

批處理

談大數據批處理,繞不過的就是 MapReduce。MapReduce 是大數據處理的老祖宗了。

2004 年 Google 發表了一篇論文 MapReduce: Simplified Data Processing on Large Clusters。論文介紹了 MapReduce 的歷史,API 的設計和實現,以及 Google 內部使用了 MapReduce 的一些生產案例,但是沒有開源程式碼。後來,開源社區參考這篇論文自己擼了一個 MapReduce 框架配合 HDFS 使用。

MapReduce 的基本思想是提供一套非常簡潔的數據處理 API —— 用戶只需要實現一個 map 函數和一個 reduce 函數。剩下的繁瑣的擴展性和容錯系統問題由 MapReduce 框架負責處理。

Map 函數的輸入是「一條記錄」,然後經過處理,輸出 0~N 個 key-value。Mapper 的輸出是 Reducer 的輸入。

Reduce 函數的輸入是「key + key 相同的所有 value」,輸出是本次 MapReduce 任務的結果。

一次 MapReduce 的執行流程如下:

  1. Map 階段:
    1. 將 Mapper 任務調度到文件分片所在的機器。
    2. 讀取文件,解析數據,然後調用 map 函數,得到輸出,根據 key 進行分片(分片數量和 Reducer 的數量一致),寫入到文件。
    3. 對得到的每個文件根據 key 進行排序。
  2. Reduce 階段:
    1. 拉取要處理的文件,進行一次合併,得到一個根據 key 排序的文件。
    2. 讀取文件,解析數據,然後調用 reduce 函數,得到輸出,寫入結果文件。

舉個例子進行說明:WordCount – 計算文件(一行一個單詞)中每個單詞出現的次數。

Map 函數:

map(String key, String value) :    // key: file name    // value: file content    for each word w in value :      EmitIntermediate(w, "1");

Reduce 函數:

reduce(String key, Interator values) :    // key: a word    // value: a list of counts    int result = 0;    for each v in values :      result += ParseInt(v);      Emit(AsString(result));
  1. Map 階段 —— 將文件內容拆成一個個單詞:
    1. 將 Mapper 任務調度到文件分片所在的機器。
    2. 讀取文件,解析數據,然後調用 map 函數,得到一個個 「word, "1"」的輸出。根據單詞進行哈希分片,寫入到文件。
    3. 對得到的每個文件根據 key 進行排序。這樣可以保證同一個單詞的 key-value 都在文件中相鄰的位置。
  2. Reduce 階段 —— 對每個單詞出現的次數進行統計:
    1. 拉取要處理的文件,進行一次合併,得到一個根據 key 排序的文件。
    2. 讀取文件,解析數據,然後調用 reduce 函數,得到輸出,寫入結果文件。

MapReduce 的優點是理解起來簡單,實現起來也不難。但是由於 MapReduce 的編程模型過於簡單,導致表達能力限制太大,單個 MapReduce 任務並不能完成大量實際上的業務需求。一些比較複雜的系統可能需要 50 ~ 100 個 MapReduce 任務進行組合,這會產生很多中間數據需要寫入到分散式文件系統,嚴重影響執行性能和效率。同時,太多的 MapReduce 任務組合提高了系統的維護難度。

關於 MapReduce 的更多細節,建議閱讀論文。

流處理

說到流處理,自然不得不提 Apache Spark 和 Apache Flink(其實我也是在網上道聽途說,這兩個系統我都不怎麼了解……)。

Spark 在 2009 年左右誕生於加州大學伯克利分校的著名 AMPLab。最開始的 Spark 其實是個批處理系統,其能成名的原因是它能夠經常在記憶體執行大量的計算工作,直到作業的最後一步才寫入磁碟,性能上比 MapReduce 要好不少。後來,Spark Streaming 的出現,Spark 才開始有了能支援流處理的能力。不過,Spark Streaming 是通過 micro-batch(多個記錄/事件) 來模擬 stream 的。從 Spark 最近的版本更新看,Spark Streaming 應該是要被新搞出來的 Structured Streaming 代替了。

和 Spark 不同,Flink 處理流的時候是 per-event 的(一個記錄/事件)。打個不太嚴謹的比方,洗頭沖水的時候有兩種方式:

  1. 拿一個杯子在水龍頭接水,再衝到頭上 => 這是 Spark 流處理的模式。
  2. 直接再水龍頭下面沖水 => 這是 Flink 流處理的模式。

小結

最後,推薦一篇論文:Google 在 VLDB2015 發表的:The Dataflow Model: A Practical Approach to Balancing Correctness, Latency, and Cost in Massive-Scale, Unbounded, Out-of-Order Data Processing。這篇論文提供了一種統一批處理和流處理的 dataflow 模型。

coredump