Spark之RDD

原文鏈接:http://spark.apache.org/docs/2.2.0/rdd-programming-guide.html

目錄

概述

一、RDD創建

Parallelized Collections

External Datasets

二、RDD操作

Transformations

Actions

三、RDD持久化


概述

在較高級別上,每個Spark應用程序都包含一個驅動程序,該程序運行在用戶main函數中並且在集羣上執行各種並行操作。Spark提供最主要的抽象是彈性分佈式數據集(RDD),它是跨集羣節點分割元素的集合,能並行操作。通過Hadoop文件系統(或者任何其他的Hadoop支持的文件系統),或者在驅動程序中已經存在的Scala集合開始並轉換創建RDDs。用戶還可以要求Spark在內存中持久化RDD,以便允許在並行操作有效的重用。最後,RDDs還可以從節點故障中自動恢復。

在Spark第二個抽象是可以在並行操作中使用共享變量。默認情況下,當Spark作爲一個任務的集合在不同的節點上並行運行一個函數時,它會將函數中使用的每個變量的副本發送到各個任務中。有時候,在任務之間或者任務和驅動程序之間共享一個變量。Spark支持兩種類型的共享變量:廣播變量(可用於所有節點的內存緩存值)和累加器(例如計數器和求和)。

一、RDD創建

Parallelized Collections

通過在驅動程序中現有的集合上調用SparkContext的Parallelize方法來創建。集合中的元素會被複製成可以並行操作的分佈式數據集。例如,創建包含數字1~5的並行集合:

val data = Array(1, 2, 3, 4, 5)
val distData = sc.parallelize(data)

一旦被創建,這個分佈式數據集就能夠被並行操作。比如你可以調用 distData.reduce((a,b) => a+b) 來求和。我們將在後面來描述分佈式數據集的操作。

並行集合一個重要的參數是數據集的分區數。Spark將會在集羣上爲每個分區運行一個任務。通常,在集羣上的每個CPU需要2-4個分區。正常情況下,Spark會基於集羣嘗試着自動設置分區數。但是,你也可以通過將它作爲 parallelize 方法的第二參數手動的設置它(例如:sc.parallelize(data, 10) )。注意:代碼的某些地方使用術語"切片"(分區的同義詞)來保持向後兼容。

External Datasets

Spark能從任何Hadoop支持的存儲源創建分佈式數據集,包括你本地的文件系統,HDFS,Cassandra,HBase, Amazon S3等等。Spark支持文本文件, SequenceFiles以及其他任何Hadoop InputFormat。

文本文件RDDs能夠使用SparkContext的textFile方法創建。此方法獲取文件的URI(機器的本地路徑或者hdfs://,s3n://等URI),並作爲行的集合讀取。以下是個實例調用:

scala> val distFile = sc.textFile("data.txt")
distFile: org.apache.spark.rdd.RDD[String] = data.txt MapPartitionsRDD[10] at textFile at <console>:26

一旦被創建,distFile就能夠通過數據集操作對其進行操作。例如,我們能夠使用 mapreduce 操作把所有行的大小加起來,代碼如下:dataFile.map(s => s.length).reduce((a,b) => a+b).

關於使用spark讀取文件的一些注意事項:

· 工作節點的路徑要和本地文件系統的路徑要相同。要麼複製文件到所有的worker節點,要麼使用網絡掛載的共享文件系統

· Spark所有基於文件輸入的方法(包括 textFile)都支持在目錄、壓縮文件和通配符上面運行。比如,你可以使用 textFile("/my/directory")textFile("/my/directory/*.txt"), and textFile("/my/directory/*.gz")

· 爲控制文件分區數,textFile 方法也支持可選的第二參數。默認情況下,Spark爲文件的每個block創建一個分區(HDFS中block默認是128M),但是你也可以通過傳更大的值來請求更大的分區數。注意:分區數不能小於block數

二、RDD操作

RDDs支持兩種操作類型:transformations(從已有的數據集創建一個新的數據集)和actions(在數據集上執行一個計算後返回值給驅動程序)。比如,maptransformation操作,它讓每個數據集元素通過一個函數並且返回一個代表結果新的RDD。另一方面,reduceaction操作,它使用某些函數聚合RDD的所有元素並且返回最終的結果給驅動程序(儘管也有並行的reduceByKey返回一個分佈式數據集)

Spark中所有的transformation是惰性的,因爲它們不會立即計算出結果。相反的,它們只記得某些基礎數據集的轉換(例如文件),transformation只有在action要求一個結果返回給驅動程序時纔會被計算。這樣的設計使得Spark可以更高效地運行。例如,我們可以認識到通過map創建的數據集將用到reduce並且僅僅返回reduce的結果給驅動程序,而不是返回較大的map數據集。

默認情況下,每次執行一個action操作時,都可能重新計算每個transformation過的RDD。但是,你也可以使用persist(或者cache)方法在內存中持久化RDD,在這種情況下,Spark將這些元素保留在集羣中,以便下次查詢時可以更快的進行訪問。還支持將RDD持久化到磁盤中,或者在多個節點之間複製。

爲了說明RDD的基礎知識,思考下以下基礎程序:

val lines = sc.textFile("data.txt")
val lineLengths = lines.map(s => s.length)
val totalLength = lineLengths.reduce((a, b) => a + b)

第一行定義了一個從外部文件生成的基礎RDD。該數據集並沒有加載到內存或者有其他作用,lines變量僅僅是指向文件的指針。第二行定義了lineLengths變量,作爲map轉換之後的結果。相同的,lineLengths由於惰性並沒有立即計算。最後,我們執行reduce(action操作)。此時,Spark將計算分解爲任務,以在不同的機器上運行,每臺機器都運行map的一部分並進行局部歸約,僅返回結果給驅動程序。

如果我們想在後面再一次用到lineLengths變量,可以添加到緩存:

lineLengths.persist()

reduce第一次執行之後,會使lineLengths變量保存到內存中。

Transformations

下面列表列出了Spark支持一些常見的Transformation操作。

操作 含義
map(func) 返回一個新的分佈式數據集,該數據集是通過將源數據集的每個元素傳遞給函數func形成的
filter(func) 返回一個新的數據集,該數據集是通過選擇源數據集中func返回true的那些元素形成的
flatMap(func) 與map相似,但是每個輸入項都可以映射到0個或多個輸出項(所以func返回的是Seq,而不是單項)
union(otherDataset) 返回一個新的數據集,其中包含源數據集中的元素和otherDataset數據集的元素的並集
distinct([numTasks])) 返回一個新的數據集,其中包含源數據集的不同元素(相同元素只保留一個)
groupByKey([numTasks])

在(K,V)鍵值對的數據集上調用時,返回(K,Iterable <V>)鍵值對的形式的數據集

注意:如果要分組以便對每個鍵執行聚合(例如求和或平均值),則使用reduceByKeyAggregateByKey將產生更好的表現

reduceByKey(func,[numTasks])

在鍵值對的數據集上調用,返回的是鍵值對的數據集

每個鍵的所有值都會使用func函數聚合,func函數必須是(v,v)=>v。和groupByKey一樣,reduce任務的數量可以通過可選的第二個參數進行配置。

sortByKey([ascending], [numTasks]) 在(K,V)形式的鍵值對上調用時,其中的K實現了Ordered,會返回一個通過鍵升序或者降序的鍵值對數據集,降序或者升序由ascending參數決定,true是升序,false是降序

Actions

下面列表列出了Spark支持一些常見的action操作。

操作 含義
reduce(func) 使用func函數(它接受兩個參數,返回一個結果)來聚合數據集中的元素。這個函數應該是可交換的和可結合的,以便它能夠正確的並行計算
collect() 在驅動程序把所有數據集的元素作爲集合返回
count() 返回數據集中元素的個數
first() 返回數據集第一個元素
take(n) 把數據集中前n個元素作爲數組返回
saveAsTextFile(path) 將數據集的元素以文本文件(或文本文件集)的形式寫入本地文件系統,HDFS或其他任何Hadoop支持的文件系統。Spark將在每個元素上調用toString,以將其轉換爲文件中的一行文本
countByKey() 僅在(K,V)形式的鍵值對的RDD可用,返回(K,int)每個鍵計數的hashmap
foreach(func) 在數據集的每個元素上執行func函數

三、RDD持久化

Spark最重要的功能之一是跨操作的在內存中持久化(或者緩存)數據集。當你持久化RDD時,每個節點會將其計算的RDD分區存儲在內存中,並在該數據集(或從該數據集派生的數據集)上的其他操作中重用它們。這樣可以使以後的速度更快(通常是10倍以上)。緩存是迭代算法和快速交互使用的關鍵工具。

你可以使用RDD的persist()或者cache()方法來將RDD持久化。當第一次在action操作被計算,它將保存在節點的內存中。Spark的緩存是容錯的——如果任意的RDD的分區丟失,它將使用最初創建它的轉換自動重新計算。

此外,每個RDD能夠使用不同的存儲等級來存儲。例如,允許你在磁盤和內存中持久化,但作爲java對象的序列化(爲節省空間)在節點之間複製。通過將StorageLevel對象傳遞給persist()來設置這些級別。 cache()方法是使用默認存儲級別StorageLevel.MEMORY_ONLY,完整的存儲級別如下:

級別 含義
MEMORY_ONLY 將RDD作爲反序列化的java對象存儲在JVM中。如果RDD不能容納在內存中,那麼一些分區不能被緩存,並且每次需要時都會即時重新計算。這是默認級別。
MEMORY_AND_DISK 將RDD作爲反序列化的java對象存儲在JVM中。如果RDD不能容納在內存中,不能容納的分區將保持在磁盤上,需要的時候再讀取。
MEMORY_ONLY_SER
(Java and Scala)
將RDD存儲爲序列化的Java對象(每個分區一個字節數組)。 通常,這比反序列化的對象更節省空間,尤其是在使用快速序列化程序時,但讀取時會佔用更多CPU資源。
MEMORY_AND_DISK_SER
(Java and Scala)
與MEMORY_ONLY_SER類似,但是將內存中不能容納的分區保存到磁盤上,而不是在需要時即時對其進行重新計算。
DISK_ONLY 只將RDD分區保存到磁盤上
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. 與上面的級別相同,但是在兩個羣集節點上覆制每個分區。
OFF_HEAP (experimental) 與MEMORY_ONLY_SER類似,但是將數據存儲在堆外存儲器中。 這需要啓用堆外內存。

翻譯水平有限,翻譯不當之處還請讀者指正!

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