Spark RDD總結

Spark RDD 總結

2019年11月21日
16:58

RDD兩種操作

RDD 支持兩種類型的操作:轉化操作(transformation)和行動操作(action)

轉化操作會由一個RDD生產一個新的RDD。 如fliter,map
行動操作會對RDD計算出一個結果。如first,count
兩種操作區別在於Spark計算RDD的方式不同。Spark惰性計算,只有第一次在一個行動操作中用到時,纔會真正計算。

RDD.persist()可以將RDD緩存下來,在多個行動操作中重用同一個RDD。

創建RDD,2種方式

  1. 讀取外部數據集 (最常用的方式)
val lines = sc.textFile("/path/to/README.md")
  1. 在驅動器程序中對一個集合進行並行化。 將一個已有的集合傳給SparkContext的parallelize()方法(除了開發原型和測試,這種方式用的不多)
val lines = sc.parallelize(List("pandas", "i like pandas"))

RDD操作

轉化操作返回的是RDD,而行動操作返回的是其他的數據類型

errorsRDD = inputRDD.filter(lambda x: "error" in x)
warningsRDD = inputRDD.filter(lambda x: "warning" in x)
badLinesRDD = errorsRDD.union(warningsRDD)

以union()爲例,轉換操作可以操作任意數量的輸入RDD

Spark會用譜系圖(lineage graph)來記錄這些不同RDD之間的依賴關係。
在這裏插入圖片描述
3-16:在Scala 中使用行動操作進行計數

println("Input had " + badLinesRDD.count() + " concerning lines")
println("Here are 10 examples:")
badLinesRDD.take(10).foreach(println)

collect函數不能用在大規模數據集上。單臺機器內存能放下數據時使用。

最好把RDD看做我們通過轉化操作構建出來的、記錄如何計算數據的指令列表。讀取數據操作也是惰性的。

向Spark傳遞函數

Python

注意:Python會在你不經意間吧函數所在的對象也序列化傳出去。
正確做法:
class WordFunctions(object):

def getMatchesNoReference(self, rdd):
 #安全:只把需要的字段提取到局部變量中
query = self.query
return rdd.filter(lambda x: query in x)
 

Scala

所傳遞的函數及其引用的數據需要是可序列化的(實現了JAVA的Serializable接口)

Scala示例

class SearchFunctions(val query: String) {
def isMatch(s: String): Boolean = {
s.contains(query)
}
def getMatchesFunctionReference(rdd: RDD[String]): RDD[String] = {
// 問題:"isMatch"表示"this.isMatch",因此我們需要傳遞整個"this"
rdd.map(isMatch)
}
def getMatchesFieldReference(rdd: RDD[String]): RDD[String] = {
// 問題:"query"表示"this.query",因此我們需要傳遞整個"this"
rdd.map(x => x.split(query))
}
def getMatchesNoReference(rdd: RDD[String]): RDD[String] = {
// 安全:只把我們需要的字字段拿出來放入局部變量中
val query_ = this.query
rdd.map(x => x.split(query_))
}
}
 

常見的轉化操作和行動操作

  1. 針對各個元素的轉化操作
    map()和filter()

map()的輸入RDD和輸出RDD可以不是同一類型
flatMap()將行數據切分爲單詞

val lines = sc.parallelize(List("hello world", "hi"))
val words = lines.flatMap(line => line.split(" "))
words.first() // 􀝓􀣮"hello"
 

在這裏插入圖片描述
2. 僞集合操作
RDD支持許多數學上的集合操作,要求操作的RDD數據類型相同。

RDD.distinct() 返回只包含不同元素的新RDD,開銷比較大
在這裏插入圖片描述
union中可包含重複數據。
intersection不包含重複數據,性能較差,因爲需要通過網絡混洗數據。
subtract和intersection一樣,也需要數據混洗。
在這裏插入圖片描述
cartesian(other)求兩個RDD的笛卡爾積,使用場景:在求用戶相似度的應用,考慮所有可能的組合的相似度等應用。
注意:求大規模RDD的笛卡爾積開銷巨大。
在這裏插入圖片描述
在這裏插入圖片描述

  1. 行動操作
reduce()
val sum = rdd.reduce((x, y) => x + y)
 
aggregate()
#計算RDD的平均值
val result = input.aggregate((0, 0))(
(acc, value) => (acc._1 + value, acc._2 + 1),
(acc1, acc2) => (acc1._1 + acc2._1, acc1._2 + acc2._2))
val avg = result._1 / result._2.toDouble

RDD的一些行動操作會以普通集合或值的形式將RDD的全部或部分數據返回驅動器程序中。
如collect(),通常在單元測試中使用
take(n)返回RDD中的n個元素,而且嘗試只訪問儘量少的分區,返回順序不確定
top()會使用數據的默認順序
takeSample(withReplacement,num,seed)函數,從數據中獲取一個採樣,並指定是否替換。
foreach()對RDD的所有元素應用一個行動操作,但不把任何結果返回到驅動器程序中。
在這裏插入圖片描述

  1. 在不同RDD類型間轉換
    有些函數只能用於特定類型的RDD,如mean()和variance()只能用在數值RDD上。Join()只能用在鍵值對RDD上。

在Scala中,將RDD轉爲有特定函數的RDD(如在RDD[Double]上進行數值操作)是由隱式轉換來自動處理的。
需要加上import org.apache.spark.SparkContext._來使用這些隱式轉換。例如隱式轉換可以把RDD[Double]轉爲DoublleRDDFunctions.查文檔時不要忘記這些封裝的專用類中的函數。

持久化(緩存)

例子:Scala中的兩次執行

val result = input.map(x => x*x)
println(result.count())
println(result.collect().mkString(","))

爲了避免多次計算同一個RDD,可以讓Spark對數據進行持久化。

默認情況下persist()會把數據以序列化的形式緩存在JVM的堆空間中。

在這裏插入圖片描述

在Scala中使用persist()

val result = input.map(x => x * x)
result.persist(StorageLevel.DISK_ONLY)
println(result.count())
println(result.collect().mkString(","))
 

RDD 還有一個方法叫做unpersist(),調用該方法可以手動把持久化的RDD從緩存中移除。

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