Learning Spark 學習筆記 第三章 RDDs編程


概述:

RDD(resilient distributed dataset,彈性分佈式數據集),是一個抽象概念,是可分佈式存儲和處理的數據的集合。spark中可進行RDD的創建;轉化已存在的RDD爲一個新的RDD;在RDD上進行分佈式處理,並行計算得到結果(value)。


RDD基礎:

RDD可分成多個分區,每個分區分佈在集羣節點上。RDDs可包含python,java,scala對象類型(包含自定義類對象)。有兩種創建方式:從外部數據集裝載;已有driver program種的一個數據list或者set對象集合。例如之前一篇中lines = sc.textFile("README.md"),用textFile創建(每行作爲一個item的RDD)

RDD創建之後有兩種操作可進行:transformations(轉換,從一個RDD經過特定處理到另一個RDD);  actions(在RDD上處理得到一個確定的結果)。

transformations:例如 val pythonLines = lines.filter(line => line.contains("Python"))//從每行作爲一個item的RDD中過濾出包含單詞Python的行構成的RDD。

actions:在RDD上處理得到一個確定的結果,結果可返回給driver program或者存到外部存儲系統(例如HDFS)。例如:pythonLines.first()就是在RDD pythonLines上進行一個action得到結果(RDD的第一個item)。

RDD架構之所以好在:惰性計算。在action階段纔對數據集進行計算。轉換階段並不對數據進行加載存儲(相反MapReduce會對每次結果進行存儲到disk中),因此較高效。例如lines = sc.textFile(...)後並不進行對每一行進行加載存儲,若進行加載存儲之後filter階段還會再一次加載存儲。而action first()只需對最終的轉換RDD處理得到第一行就可以,無需讀取整個文件。

在第一次對RDD進行action之後,spark會存儲RDD的內容到內存中(也是分佈式存儲到集羣上)而不是disk中。RDD.persist()操作會把數據持久化到disk中,便於多次對RDD進行action(我理解爲:RDD1->RDD2->RDD3,在RDD3上進行action時才進行RDD1到RDD3的轉換並做相應action的計算,此時如果再次action,那麼會重新從RDD1來一次。如果RDD2進行持久化後,從RDD2開始就可以了),例如: pythonLines.persist  pythonLines.count()  pythonLines.first()。若不會重複使用RDD則無需進行持久化而浪費存儲空間。

因此,每個spark程序或shell會話如下流程:

1.從外部數據中創建RDDs

2.對RDDs進行轉換成新的RDDs,例如filter()操作

3.若要對RDDs進行重複使用,則需persist()持久化

4.actions例如count(),first(),啓動並行的計算,spark會對計算任務進行優化和執行。

創建RDDs:

一:把一個已存在的集合傳給SparkContext的parallelize()方法得到。例如 val lines = sc.parallelize(List("pandas", "i like pandas"))

二:從外部數據集裝載。詳細會在第五章講,但是之前我們已經見識過一種文件中得到RDD的方式(每行作爲一個item,數據的inputformat決定):val lines = sc.textFile("/path/to/README.md")

RDD的操作:

RDD支持transformations(RDDs轉爲新的RDDs,例如map(),filter()操作)和actions(在集羣上啓動一個分佈式計算得到一個結果返回給driber program或存儲起來,例如count(),first()操作)。區分操作是transformations還是actions:返回類型爲RDDs爲轉換,爲其他數據類型是actions。

transformations:例如一個logfile inputRDD,我們通過filter進行過濾轉換出含有error的errorsRDD,之後我們還可以filter出含有warning的warningsRDD,新的RDD都會存於內存中,同樣我們可以依賴於errorsRDD和warningsRDD進行轉換操作(badLinesRDD = errorsRDD.union(warningsRDD)),如下圖所示,spark會記錄此過程:



因此若某個環節的RDD持久化的數據損壞了,還可以進行修復,這也是spark的容錯機制。

actions:例如如下

println("Input had " + badLinesRDD.count() + " concerning lines")
println("Here are 10 examples:")
badLinesRDD.take(10).foreach(println)
//take收集十個item到driver program(返回結果到driver program)

driver program通常部署在name node,collect() action可以收集整個RDD到driver program,若你的RDD小數據量,且你希望在本地進行處理可進行collect,若大數據不可,你可以通過saveAsTextFile()或saveAsSequenceFile()或者其他formats的存儲到系統(HDFS或者S3中)。需要注意的是,每次action都會使RDD重頭開始計算,因此可以通過持久化中間結果來避免效率低下。

惰性計算:每次RDD轉換並不會立即計算,在遇到action時才計算。RDD可想象成記錄怎麼轉換數據的操作,而不是數據集合,這樣會把轉換操作集中,減少數據傳遞。

Passing Functions to Spark:此小節看不懂啥意思啊!望有人看到可以指導下

常用的一些transformations和actions:

最常用的transformations爲map和filter:


map可理解爲MapReduce中的map階段,map的輸入和輸出類型可不同,inputRDD爲string,map出的RDD可爲double等。filter就是過濾出一個子集RDD。flatMap()返回values的迭代器,功能可用來把string分爲words:

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

map和flatmap區別如下圖所示:

RDD1中“”中爲一個item,操作tokenize是把每個item list化,map的結果是RDD1的一個item編程mappedRDD中一個item(是一個list[“”,“”]),flagmap則把RDD1的每個item得到的list再進行flattening,每個單詞爲一個item。

RDD還支持一些數學集合操作:


distinct進行去重,union是並集,intersection是交集,subtract差集。


cartesian(other)是求兩個RDD item的所有組合。

常用的actions:

reduce():val sum = rdd.reduce((x, y) => x + y),類似於MapReduce的reduce概念。

若RDD {1,2,3,3},如下action:




圖中很容易理解,不一一解釋了。


Converting Between RDD Types 小節暫沒看。


持久化:

spark有如下持久化選擇:

使用如下(在action之前進行持久化,unpersist()從cache移除):

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

若你試圖持久化很多數據,使用LRU策略進行內存管理。

總結:

對RDD有了又一步深入的瞭解,但是還不夠透徹,有的地方可能自己理解的也有錯誤,接受指正。看完這本書之後再去看別的加以深入。

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