Spark RDD基本操作

Spark RDD Scala語言編程


RDD(Resilient Distributed Dataset)是一個不可變的分佈式對象集合, 每個rdd被分爲多個分區, 這些分區運行在集羣的不同節點上。rdd支持兩種類型的操作:轉化(trainsformation)和行動(action), Spark只會惰性計算rdd, 也就是說, 轉化操作的rdd不會立即計算, 而是在其第一次遇到行動操作時纔去計算, 如果想在多個行動操作中複用一個rdd, 可以使用RDD.persist()讓Spark把這個rdd緩存下來。

0. 初始化SparkContext

import org.apache.spark.{SparkConf, SparkContext}

val sc = new SparkContext(new SparkConf().setMaster("local[*]").setAppName("spark-rdd-demo"))

1. 創建RDD

Spark提供了2種創建RDD的方式:

1.1 讀取外部數據集
val lines = sc.parallelize(List("Java", "Scala", "C++"))
1.2 在驅動器程序中對一個集合進行並行化
val lines = sc.textFile("hdfs://dash-dev:9000/input/test.txt")

2. RDD操作

2.1 轉化操作

RDD的轉化操作是返回新RDD的操作, 常用轉化操作總結如下:

表1: 對一個數據爲{1,2,3,3}的RDD進行基本的轉化操作

函數名 目的 示例 結果
map() 將函數應用於RDD中每個元素, 將返回值構成新的RDD rdd.map(x=>x+1) {2,3,4,5}
flatMap() 將函數應用於RDD中的每個元素, 將返回的迭代器的所有內容構成新的RDD, 常用來切分單詞 rdd.flatMap(x=>x.to(2)) {1,2,2}
filter() 返回一個通過傳入給filter()的函數的元素組成的RDD rdd.filter(x=> x>2) {3,3}
distinct() 去重 rdd.distinct() {1,2,3}
sample(withReplacement, fraction, [seed]) 對RDD採樣, 以及是否替換 rdd.sample(false, 0.5) 非確定的

表2: 對數據分別爲{1,2,3}和{2,3,4}RDD進行鍼對2個RDD的轉化操作

函數名 目的 示例 結果
union() 求2個RDD的並集 rdd.union(other) {1,2,3,4}
intersection() 求2個RDD的交集 rdd.intersection(other) {2,3}
subtract() 求2個RDD的差集 rdd.subtract(other) {1}
cartesian() 求2個RDD的笛卡爾積 rdd.cartesian(other) {1,2}, {1,3}, {1,4}…{3,4}
sample(withReplacement, fraction, [seed]) 對RDD採樣, 以及是否替換 rdd.sample(false, 0.5) 非確定的
2.2 行動操作

RDD的行動操作會把最終求得的結果返回驅動器程序, 或者寫入外部存儲系統中。

表3: 對一個數據爲{1,2,3,3}的RDD進行基本RDD的行動操作

函數名 目的 示例 結果
redcue() 並行整合RDD中的所有元素 rdd.reduce((x, y) => x+y) 9
collect() 返回RDD中的所有元素 rdd.collect() {1,2,3,4}
count() 求RDD中的元素個數 rdd.count() 4
countByValue() 各元素在RDD中出現的次數 rdd.countByValue() {1,1}, {2, 1}, {3,2}
take(n) 從RDD中返回n個元素 rdd.take(2) {1,2}
top(n) 從RDD中返回前n個元素 rdd.top(3) {3,3,2}
foreach(func) 對RDD中的每個元素使用給定的函數 rdd.foreach(print) 1,2,3,3
2.3 向Spark傳遞函數

Spark的大部分轉化和行動操作都要依賴於用戶傳遞的函數來計算, 當傳遞的對象是某個對象的成員, 或者包含了對某個對象中一個字段的引用時(如self.field), Spark就會把整個對象發送到工作節點上--這比你本意想傳遞的東西大太多了!替代的方案是,把你需要的字段從對象中拿出來放到一個局部變量中, 然後傳遞這個局部變量:

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 localQuery = this.query
        rdd.map(x => x.split(localQuery))
    }
}

另外, 要注意的是, Spark要求我們傳入的函數及其應用的數據是可序列化的(實現了Java的Serializable接口), 否則會出現NotSerializableException。

作者 @wusuopubupt
2016年11月11日

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