spark學習筆記--RDD

zxrui發表於2018-07-05

玩轉RDD

  • RDD操作包含兩種運算元,transformation(轉化)和action(行動)
  • 只有在行動運算元下才會進行真正的資料運算,因為spark的運算機制是將資料存入記憶體,大資料處理考慮效率,如此操作是合理的
  • rdd.persist() 可以將rdd持久化在記憶體中

1. 建立RDD

Spark 提供了兩種建立 RDD 的方式:讀取外部資料集,以及在驅動器程式中對一個集合進行並行化( parallelize() 方法)。

val lines = sc.textFile("/path/to/README.md")

2. RDD操作

轉化操作

RDD的轉化操作是惰性求值的

針對各個元素的轉化

  • map() (接收一個函式,把這個函式用於 RDD 中的每個元素,將函式的返回結果作為結果 RDD 中對應元素的值)

scala:

val input = sc.parallelize(List(1, 2, 3, 4))  
val result = input.map(x => x * x)  
println(result.collect().mkString(","))

-flatmap()(我們提供給 flatMap() 的函式被分別應用到了輸入 RDD 的每個元素上。不過返回的不是一個元素,而是一個返回值序列的迭代器)

scala:

val lines = sc.parallelize(List("hello world", "hi"))  
val words = lines.flatMap(line => line.split(" "))  
words.first() // 返回"hello"
  • filter() (接收一個函式,並將 RDD 中滿足該函式的元素放入新的 RDD 中返回)

scala:

val inputRDD = sc.textFile("log.txt")  
val errorsRDD = inputRDD.filter(line => line.contains("error"))

filter() 操作不會改變已有的 inputRDD 中的資料。實際上,該操作會返回一個全新的 RDD

  • distinct()(生成一個只包含不同元素的新 RDD)

    distinct() 操作的開銷很大,因為它需要將所有資料通過網路進行混洗(shuffle),以確保每個元素都只有一份

  • sample(withReplacement, fraction, [seed])

    對 RDD 取樣,以及是否替換

偽集合操作

  • union()

scala:

badLinesRDD = errorsRDD.union(warningsRDD)

union() 與 filter() 的不同點在於它操作兩個 RDD 而不是一個。轉化操作可以操作任意數量的輸入 RDD。

  • intersection()

    只返回兩個 RDD 中都有的元素,執行時也會去掉所有重複的元素(單個 RDD 內的重複元素也會一起移除)

  • subtract()

    函式接收另一個RDD作為引數,返回一個由只存在於第一個 RDD 中而不存在於第二個 RDD 中的所有元素組成的 RDD

  • cartesian()

    返回所有可能的對,即進行笛卡爾積操作

最後要說的是,通過轉化操作,你從已有的 RDD 中派生出新的 RDD,Spark 會使用譜系圖(lineage graph)來記錄這些不同 RDD 之間的依賴關係。Spark 需要用這些資訊來按需計算每個 RDD,也可以依靠譜系圖在持久化的 RDD 丟失部分資料時恢復所丟失的資料

行動操作

  • reduce()

    接收一個函式作為引數,這個函式要操作兩個相同元素型別的 RDD 資料並返回一個同樣型別的新元素

scala:

val sum = rdd.reduce((x, y) => x + y)
  • fold()

    接收一個與 reduce() 接收的函式簽名相同的函式,再加上一個“初始值”來作為每個分割槽第一次呼叫時的結果

  • aggregate()

    aggregate() 函式則把我們從返回值型別必須與所操作的 RDD 型別相同的限制中解放出來;
    需要提供我們期待返回的型別的初始值。然後通過一個函式把 RDD 中的元素合併起來放入累加器。考慮到每個節點是在本地進行累加的,最終,還需要提供第二個函式來將累加器兩兩合併。

scala:

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
  • collect()

    collect() 不能用在大規模資料集上,因為其可以用來獲取整個 RDD 中的資料。如果你的程式把 RDD 篩選到一個很小的規模,並且你想在本地處理這些資料時,就可以使用它。謹記:只有當你的整個資料集能在單臺機器的記憶體中放得下時,才能使用 collect()
    資料集通常很大,我們通常要把資料寫到諸如 HDFS 或 Amazon S3 這樣的分散式的儲存系統中。你可以使用 saveAsTextFile()、saveAsSequenceFile(),或者任意的其他行動操作來把 RDD 的資料內容以各種自帶的格式儲存起來

  • count()

  • countByValue()

    各元素在 RDD 中出現的次數

  • take()

  • top()

  • takeOrdered(num)(ordering)

    從 RDD 中按照提供的順序返回最前面的 num 個元素

  • takeSample(withReplacement, num, [seed])

    從 RDD 中返回任意一些元素

  • foreach(func)

    對 RDD 中的每個元素使用給定的函式

enter image description here

記憶體持久化

Spark RDD 是惰性求值的,而有時我們希望能多次使用同一個 RDD。如果簡單地對 RDD 呼叫行動操作,Spark 每次都會重算 RDD 以及它的所有依賴。這在迭代演算法中消耗格外大,因為迭代演算法常常會多次使用同一組資料。為了避免多次計算同一個 RDD,可以讓 Spark 對資料進行持久化

scala:

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

-unpersist()

手動把持久化的 RDD 從快取中移除

相關文章