Spark:超越Hadoop MapReduce
和 Hadoop 一樣,Spark 提供了一個 Map/Reduce API(分布式計(jì)算)和分布式存儲。二者主要的不同點(diǎn)是,Spark 在集群的內(nèi)存中保存數(shù)據(jù),而 Hadoop 在集群的磁盤中存儲數(shù)據(jù)。
大數(shù)據(jù)對一些數(shù)據(jù)科學(xué)團(tuán)隊(duì)來說是 主要的挑戰(zhàn),因?yàn)樵谝蟮目蓴U(kuò)展性方面單機(jī)沒有能力和容量來運(yùn)行大規(guī)模數(shù)據(jù)處 理。此外,即使專為大數(shù)據(jù)設(shè)計(jì)的系統(tǒng),如 Hadoop,由于一些數(shù)據(jù)的屬性問題也很難有效地處理圖數(shù)據(jù),我們將在本章的其他部分看到這方面的內(nèi)容。
Apache Spark 與 Hadoop 類似,數(shù)據(jù)分布式存儲在服務(wù)器的集群或者是“節(jié)點(diǎn)”上。 不同的是,Spark 將數(shù)據(jù)保存在內(nèi)存(RAM)中,Hadoop 把數(shù)據(jù)保存在磁盤(機(jī)械 硬盤或者 SSD 固態(tài)硬盤)中。
定義 :在圖和集群計(jì)算方面,“節(jié)點(diǎn)”這個詞有兩種截然不同的意思。 圖數(shù)據(jù)由頂點(diǎn)和邊組成,在這里“節(jié)點(diǎn)”與頂點(diǎn)的意思相近。在集群計(jì)算 方面,組成集群的物理機(jī)器也被稱為“節(jié)點(diǎn)”。為避免混淆,我們稱圖的 節(jié)點(diǎn)為頂點(diǎn),這也是 Spark 中的專有名詞。而本書中的“節(jié)點(diǎn)”這個詞我 們嚴(yán)格定義為集群中的單個物理計(jì)算節(jié)點(diǎn)。
大數(shù)據(jù)因?yàn)閿?shù)據(jù)量大單機(jī)無法處理。Hadoop 和 Spark 都是把數(shù)據(jù)分布在集群節(jié)點(diǎn)上的分 布式框架中。Spark 把分布式數(shù)據(jù)集存放在內(nèi)存中,所以比 Hadoop 把數(shù)據(jù)存放在磁盤中 處理速度要快很多。
除了將要計(jì)算的數(shù)據(jù)保存的位置不同(內(nèi)存和磁盤),Spark 的 API 比 Hadoop的 Map/Reduce API 更容易使用。Spark 使用簡潔且表達(dá)力較好的 Scala 作為原生編程語言,寫 Hadoop Map/Reduce 的 Java 代碼行數(shù)與寫 Spark 的 Scala 的代碼行的數(shù) 量比一般是 10:1。
雖然本書主要使用 Scala,但是你對 Scala 不熟悉也不用擔(dān)心,我們在第 3 章提 供了快速入門,包括怪異、晦澀和簡練的 Scala 語法。進(jìn)一步熟悉 Java、C++、C#、 Python 等至少一門編程語言是必要的。
模糊的大數(shù)據(jù)定義
現(xiàn)在的“大數(shù)據(jù)”概念已經(jīng)被很大程度地夸大了。大數(shù)據(jù)的概念可以追溯到Google 在 2003 年發(fā)表的 Google 文件系統(tǒng)的論文和 2004 年發(fā)表的 Map/Reduce 論文。
大數(shù)據(jù)這個術(shù)語有多種不同的定義,并且有些定義已經(jīng)失去了大數(shù)據(jù)所應(yīng)有的意 義。但是簡單的核心且至關(guān)重要的意義是:大數(shù)據(jù)是因數(shù)據(jù)本身太大,單機(jī)無法處理。
數(shù)據(jù)量已經(jīng)呈爆炸性增長。數(shù)據(jù)來自網(wǎng)站的點(diǎn)擊、服務(wù)器日志和帶有傳感器的 硬件等,這些稱為數(shù)據(jù)源。有些數(shù)據(jù)是圖數(shù)據(jù)(graph data),意味著由邊和頂點(diǎn)組成, 如一些協(xié)作類網(wǎng)站(屬于“Web 2.0”的社交媒體的一種)。大的圖數(shù)據(jù)集實(shí)際上是 眾包的,例如知識互相連接的 Wikipedia、Facebook 的朋友數(shù)據(jù)、LinkedIn 的連接數(shù) 據(jù),或者 Twitter 的粉絲數(shù)據(jù)。
Hadoop :Spark 之前的世界
在討論 Spark 之前,我們總結(jié)一下 Hadoop 是如何解決大數(shù)據(jù)問題的,因?yàn)镾park 是建立在下面將要描述的核心 Hadoop 概念之上的。
Hadoop 提供了在集群機(jī)器中實(shí)現(xiàn)容錯、并行處理的框架。Hadoop 有兩個關(guān)鍵 能力 :
- HDFS—分布式存儲
- MapReduce—分布式計(jì)算
HDFS 提供了分布式、容錯存儲。NameNode 把單個大文件分割成小塊,典型 的塊大小是 64MB 或 128MB。這些小塊文件被分散在集群中的不同機(jī)器上。容錯性 是將每個文件的小塊復(fù)制到一定數(shù)量的機(jī)器節(jié)點(diǎn)上(默認(rèn)復(fù)制到 3 個不同節(jié)點(diǎn), 下圖中為了表示方便,將復(fù)制數(shù)設(shè)置為 2)。假如一個機(jī)器節(jié)點(diǎn)失效,致使這個機(jī)器上的 所有文件塊不可用,但其他機(jī)器節(jié)點(diǎn)可以提供缺失的文件塊。這是 Hadoop 架構(gòu)的 關(guān)鍵理念 :機(jī)器出故障是正常運(yùn)作的一部分。
三個分布式數(shù)據(jù)塊通過 Hadoop 分布式文件系統(tǒng)(HDFS)保持兩個副本。
MapReduce 是提供并行和分布式計(jì)算的 Hadoop 并行處理框架,如下圖 。
MapReduce 是被 Hadoop 和 Spark 都用到的一個數(shù)據(jù)處理范式。圖中表示計(jì)算服務(wù)器日 志文件中“error”出現(xiàn)的次數(shù),這是一個 MapReduce 操作。通常 Map 操作是一對一的 操作,對每一個源數(shù)據(jù)項(xiàng)生成一個相應(yīng)的數(shù)據(jù)轉(zhuǎn)換操作。Reduce 是多對一的操作,聚合 Map 階段的輸出。Hadoop 和 Spark 都用到了 MapReduce 范式。
用 MapReduce 框架,程序員寫一個封裝有 map 和 reduce 函數(shù)的獨(dú)立代碼片段來處 理 HDFS 上的數(shù)據(jù)集。為取到數(shù)據(jù)位置,代碼打包(jar 格式)分發(fā)到數(shù)據(jù)節(jié)點(diǎn), Map 操作就在這些數(shù)據(jù)節(jié)點(diǎn)上執(zhí)行,這避免了集群的數(shù)據(jù)傳輸導(dǎo)致消耗網(wǎng)絡(luò)帶寬。 對于 Reduce 聚合操作,Map 的結(jié)果被傳輸?shù)蕉鄠€ Reduce 節(jié)點(diǎn)上做 reduce 操作(稱 之為 shuffling)。首先,Map 階段是并行操作的,Hadoop 提供了一個彈性機(jī)制,當(dāng) 一個機(jī)器節(jié)點(diǎn)或者一個處理過程失敗時,計(jì)算會在其他機(jī)器節(jié)點(diǎn)上重啟。
MapReduce 編程框架將數(shù)據(jù)集抽象為流式 key-value 鍵值對,然后處理這些鍵 值對并寫回到 HDFS。這是一個有局限的范式,但它已被用來解決許多數(shù)據(jù)并行問題, 用鏈接在一起的 MapReduce 進(jìn)行“讀-處理-寫”操作過程。對于一些簡單的任務(wù),上圖顯示的是比較適合的場景。但是對于一些如機(jī)器學(xué)習(xí)算法中的迭代計(jì)算算 法,用這種 MapReduce 范式就很痛苦,這也是選擇使用 Spark 的原因。
Spark :內(nèi)存中的 MapReduce 處理
我們來看另一個可選的分布式處理系統(tǒng),構(gòu)建在 Hadoop 基礎(chǔ)之上的 Spark。在這一小節(jié)你會了解到,在 Spark 處理圖數(shù)據(jù)時扮演重要角色的彈性分 布式數(shù)據(jù)集(RDD)導(dǎo)致 Hadoop 衰落的兩類問題是 :
- 交互式查詢
- 迭代算法
Hadoop 很適合在一個大的數(shù)據(jù)集上做單次查詢,而在許多實(shí)際場景中,一旦有 了一個想要的答案,我們就想再問數(shù)據(jù)一個問題,這就是交互式查詢。使用 Hadoop 的話,就意味著要等待重新從磁盤中加載數(shù)據(jù),再次處理數(shù)據(jù)。我們不得不執(zhí)行一 組相同的計(jì)算作為隨后分析的前提,這不符合常理。
迭代算法已經(jīng)被廣泛應(yīng)用于機(jī)器學(xué)習(xí)任務(wù),如隨機(jī)梯度下降算法,以及之后 會看到的 PageRank 這類圖計(jì)算算法。迭代算法是在一個數(shù)據(jù)集上一遍又一遍地做 一組計(jì)算,直到滿足一個標(biāo)準(zhǔn)(循環(huán)結(jié)束條件)才結(jié)束迭代。 在 Hadoop 中實(shí)現(xiàn)這種算法,一般需要一系列加載數(shù)據(jù)的 MapReduce 任務(wù),這些 MapReduce 任務(wù)要在 每一個迭代過程中重復(fù)運(yùn)行。對于非常大的數(shù)據(jù)集,每個迭代過程要花費(fèi) 100 秒或1000 秒,整個迭代過程非常耗時。
下面你會看到 Spark 如何解決這些問題。如 Hadoop 一樣,Spark 也是運(yùn)行在 一個常見的硬件配置的機(jī)器集群上。Spark 中的一個核心抽象是彈性分布式數(shù)據(jù)集(RDD)。RDD 是由 Spark 應(yīng)用創(chuàng)建的(在Spark Driver上),由集群管理,如下圖。
Spark 提供一個彈性分布式數(shù)據(jù)集,可以認(rèn)為它是一個分布式的常駐內(nèi)存的數(shù)組。
組成 RDD 分布式數(shù)據(jù)集的數(shù)據(jù)分區(qū)會被加載到集群的機(jī)器上。
基于內(nèi)存的數(shù)據(jù)處理
Spark 執(zhí)行的大部分操作都是在隨機(jī)訪問內(nèi)存中(RAM)進(jìn)行。Spark 是基于內(nèi) 存的,而 Hadoop Map/Reduce 是順序處理數(shù)據(jù),所以 Spark 比 Hadoop 更適合處理 隨機(jī)訪問的圖數(shù)據(jù)。
Spark 的關(guān)鍵好處在于交互式查詢和迭代處理過程中在內(nèi)存中緩存 RDD。緩存 起來的 RDD 可以避免每次重新處理父 RDD 鏈,而只需要直接返回父 RDD 計(jì)算后 的緩存結(jié)果。
自然的,這意味著要用到 Spark 的基于內(nèi)存的計(jì)算處理特性,要求集群中的機(jī) 器內(nèi)存要足夠大。要是可用內(nèi)存不夠,那么 Spark 就會優(yōu)雅地溢出數(shù)據(jù)到磁盤,以 保證 Spark 能繼續(xù)運(yùn)行。
當(dāng)然 Spark 集群也需要一個持久化存儲數(shù)據(jù)的地方,而且還要是分布式存儲系 統(tǒng)才行,可選的有 HDFS、Cassandra 和亞馬遜的 S3。