Spark入門-常用函數彙總

Photo by Simon Migaj from Pexels

Spark 是一個分佈式的計算系統,而且函數式編程風格使在Spark上開發任務變得更有效率。

參加工作後使用Spark開發維護了四個算法,雖然算法不同但Spark代碼中所用的幾個函數卻一樣。對於新手入門Spark編程,掌握這幾個函數就夠了。

在介紹這幾個函數之前,先介紹Spark最重要的兩個概念。

1. RDD,即分佈式數據集合

就相當於是把數據分成幾份,分別存儲在不同的機器上。很多操作是作用在數據集的單個元素上,所以可以讓機器對各自擁有的數據做處理就行,這就大大加快了程序運行的時間。

2. 惰性求值

Spark操作分爲兩類,一是轉化操作,二是行動操作。只有當出現行動操作時前面的轉化操作纔會被真正執行,而且不會將中間狀態的數據保存在內存中。

比如有兩個操作,大致表述成這樣: a = 1, b = a+1, c = b+1, print(c),這裏就暫時讓print作爲執行操作存在。若是Python,則a、b、c都會佔用內存資源,但在Spark中卻不是的。當計算完c後,b就會被踢出去,而print(c)之後,c也會被踢出去,這就節省了大量的資源。

當然,若是你希望保留某個中間值以避免重複計算,Spark也提供支持函數。

常用函數

最基本的二個轉化操作是 map、filter。

map 的作用是獲得需要的字段或對單個元素進行操作。比如RDD[(Long, Long, Long)]類型的數據-即每一條記錄有三個字段,每個字段的類型是長整型。我們只需要保留第一個字段,並轉化成字符串類型,那麼我們可以用過 .map(x => x._1.toString)來實現。

filter 的作用是過濾掉不需要的數據。比如我們只想保留上述數據集中第一個字段爲正數的數據,那可以通過.filter(x => x._1 > 0)來實現。

有時我們需要合併兩份相同類型的數據集,通過a.union(b)即可完成。

接下來介紹兩個強大並且常用的函數 flatMap 和 reduceByKey。

flatMap 的作用是把一份數據集拆散壓扁,常常和 split 函數共同使用。比如我們現在有一份數據RDD[String],其中有些元素是以逗號分隔的字符,我們希望每一個被分隔的字符都能做爲獨立的數據存在。在 Spark 中我們只需要這麼做:.flatMap(x => x.split(","))x.split(",")將字符轉化成一個數組,這和其它語言中一樣,然後 flatMap 會把數組中每一個元素拆出來。

reduceByKey 是一個聚合函數,它會對擁有相同 key 的元素進行某些操作。像RDD[(String,String)]``的數據類型,第一個字段會被當做 key。所以 ``map可以通過調整字段的順序來指定 key。

接着上面的函數講,拆完之後,若是想統計每個字符出現的次數,我們就可能通過 reduceByKey 來實現。使用.map(x => (x, 1)).reduceByKey((a,b) => a+b)即可完成此操作。map 的目的是讓每個字符作爲一個 key ,然後 reduceByKey 來計數,a、b就是每個key當前統計的數量。

由於是分佈式數據集,reduceByKey 會在各個機器上對當前的數據做計數操作,然後再合併各個機器上的數據。

在現實生活中,很多數據都是以 key-value 結構存在的,而有些操作只需要對value進行即可,比如RDD[(String,String)]``中,我們只想對第二個字段做 split 操作,原先我們可以通過.map(x => (x._1, x._2.split(","))實現。但Spark提供的更簡便的方式:.mapValues( x => x.split(","))```。後一種方式只對 value 做操作,而忽略 key。

同樣,我們可以使用 flatMapValues 對value進行扁平化操作。

排序是始終繞不開的話題。Spark 中 可以使用 sortBy 來進行排序。比如上文中提到的類型RDD[(Long, Long, Long)],若是需要按第三個字段來降序排序,我們可以這麼做: .sortBy(_._3, false)

最常見的執行操作是 .collect(),它的作用僅僅是觸發執行操作用,讓前面的轉化操作行動起來。比如RDD[String]類型的數據集,我們可以通過.map(x => (x, 1)).reduceByKey((a,b) => a+b).collect().foreach(x => println(x._1 + "的數量:" + x._2.toString ))來打印所有的字符的數量。若是拿掉 collect() 這個操作,該語句就不會被執行。

與collect有共樣作用的函數是 take,但take只用獲取你需要數據的元素,比如.map(x => (x, 1)).reduceByKey((a,b) => a+b).take(5).foreach(x => println(x._1 + "的數量:" + x._2.toString ))則最多會打印五條記錄。

Spark爲了節省內存資源,執行操作後不會保留中間數據,這可能會帶來重複計算的問題。Spakr爲了解決這個問題,提供了一個函數:cache,它能幫助你保留中間數據。

結語

由於採用函數式編程,代碼會變得更便捷,但這可能會讓新手看得雲裏霧裏,覺得“難”就產生了抗拒,但其實只要熟悉了上面的幾個函數後,就會覺得自己怎麼沒早點學Spark。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章