【大資料】Spark RDD基礎

菜丸子發表於2019-01-03

一 RDD基礎

1 sparkContext

每個Spark應用都由一個驅動器程式來發起叢集上的各種並行操作,驅動器程式通過一個SparkContext物件訪問Spark。有了SparkCont,我們就可以用它建立RDD。
如何初始化sparkContext: 我們只需要兩個引數,叢集URL和應用名。
例子2-9

2 RDD

彈性分散式資料集,一個不可變的分散式物件集合,每個RDD都被分為多個分割槽,這些分割槽執行在叢集中的不同節點上。
在Spark中,對資料的所有操作就是建立RDD,轉化已有RDD,呼叫RDD操作進行求值。

使用者可以有兩種方式建立RDD

⑴讀取一個外部資料集
⑵在驅動器程式分佈驅動器程式中的物件集合(比如list和set)

RDD支援兩種型別的操作

⑴轉換操作(transformation) 由一個RDD生成一個新的RDD,比如我們用filter來篩選資料。轉化操作的計算必須得等到有了行動操作才行。
⑵行動操作(action) 對RDD計算出一個結果,並把結果返回到驅動器程式中,或者把結果儲存到外部系統(如HDFS中)。first()方法可以返回RDD的第一個元素,他就是一個行動操作。

二 建立RDD

1 讀取外部資料集
2 在驅動器程式中對一個集合進行並行化

主要用於測試 例3-6

三 RDD操作

1 轉化操作

RDD轉化操作是返回新RDD的操作,比如我們的filter()操作並不會改變原來RDD的資料。Spark會用譜系圖來記錄這些不同RDD的依賴關係。

2 行動操作

對資料集進行實際的計算
每當我們呼叫一個新的行動操作時,整個RDD都會從頭開始計算,為了避免這種低效行為,我們可以將中間結果持久化。
action 預設是fifo,我們也可以選擇fair,job0 job1並行,有的時候job0的結果還沒算出快取,job1就找不到快取,就找job0的上一個,直到hdfs.

3 惰性求值

RDD的轉化操作都是惰性求值的,意味著我們對RDD呼叫轉化操作時,操作不會立即執行,這樣可以節約記憶體。我們可以使用簡單的action操作,比如count來計劃轉化操作。

四 持久化

RDD是惰性求值的,有時候我們希望能多次使用同一個RDD,如果進行重算操作消耗很大,這時候我們就可以使用持久化方式

1. 持久化persist()
持久化策略:
⑴MEMORY_ONLY

使用未序列化的Java物件格式,將資料儲存在記憶體中。如果記憶體不夠存放所有的資料,那麼下次對這個RDD執行運算元操作時,那些沒有被持久化的資料,需要從源頭處重新計算一遍。這是預設的持久化策略,使用cache()方法時,實際就是使用的這種持久化策略。

⑵MEMORY_AND_DISK

使用未序列化的Java物件格式,優先嚐試將資料儲存在記憶體中。如果記憶體不夠存放所有的資料,會將資料寫入磁碟檔案中

⑶MEMORY_ONLY_SER

會將RDD中的資料進行序列化。

⑷MEMORY_AND_DISK_SER

會將RDD中的資料進行序列化。

⑸DISK_ONLY

使用未序列化的Java物件格式,將資料全部寫入磁碟檔案中。

⑹MEMORY_ONLY_2, MEMORY_AND_DISK_2, 等等

對於上述任意一種持久化策略,如果加上字尾_2,代表的是將每個持久化的資料,都複製一份副本,並將副本儲存到其他節點上。這種基於副本的持久化機制主要用於進行容錯。

資料淘汰機制

若我們都存在記憶體中,記憶體中放不下,會使用LRU策略,把最近最少使用的快取移除

2. 反持久化unpersist()

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

五 向Spark中傳遞函式

主要有下面兩種方式,在JAVA8中可以使用lambda來簡化

Function    R  call(T)   接收一個輸入值,並返回一個輸出值,用於類似map()filter()方法  Function2   R  call(T1, T2)  接收兩個輸入值並返回一個輸出值,用於類似於aggregate()fold()等操作  
FlatMapFunction  Iterable<R>  call(T)  接收一個輸入值並返回任意個輸出,用於類似flatMap()
函式名 實現的方法 用途
Function<T,R> R call(T) 接收一個輸入值,並返回一個輸出值,用於類似map()和filter()方法
Function2<T1,T2,R> R call(T1, T2) 接收兩個輸入值並返回一個輸出值,用於類似於aggregate()和fold()等操作
FlatMapFunction<T,R> Iterable call(T) 接收一個輸入值並返回任意個輸出,用於類似flatMap()
1.使用匿名內部類
RDD<String> errors = lines.filter(new Function<String, Boolean>() {
public Boolean call(String x) {
return x.contains("error");
}
});
2.具名類
class Contains implements Function<String, Boolean>() {
private String query;
public Contains(String query) { this.query = query; }
public Boolean call(String x) { return x.contains(query); }
}
RDD<String> errors = lines.filter(new Contains("error"));

一般使用具名類我們可以進行引數傳遞

3.lambda表示式
RDD<String> errors = lines.filter(s -> s.contains("error"));

六 常見的轉化和行動操作

1.基本RDD
⑴filter

轉化接受一個函式,將RDD中滿足該函式的元素放入新RDD中返回

⑵map

map接受一個函式,把這個函式用於RDD的每個元素,將函式的返回結果作為結果RDD中對應元素的值,map的返回值型別不需要和輸入型別一樣

⑶flatMap

有時我們需要將每個輸入元素生成多個輸出元素,比如WordCount中將字串切分為單詞,實現該功能的操作叫做flatMap(),flatMap()返回的是一個返回值序列的迭代器。輸出RDD是一個包含各個迭代器可訪問的所有元素RDD

2.偽集合操作
⑴distinct

如果一個RDD有重複元素,我們可以使用distinct方法來生成一個只包含不同元素的新的RDD,它需要對所有資料進行shuffle,所以開銷大。

⑵union

它會返回包含兩個RDD所有元素的RDD

⑶intersection

只返回兩個RDD都有的元素,它也需要shuffle

⑷subtract

返回只存在第一個而不存在第二個的

⑸cartesian

笛卡爾積,開銷巨大

3.行動操作

七 在不同的RDD型別間轉換

還未整理:

每個程式都有一個driver。
spark的job任務切分為多個task分佈在叢集裡的多臺機器,發配到物理節點,task就將那個物理節點磁碟資料載入到RAM裡面。
不同的job的driver驅動程式在不同的機器上。

流程示意圖
一切資料過來都封裝成RDD,
然後進行RDD轉換,即運算元操作
1.transformation 延遲執行
2.action觸發執行
spark碰到action運算元才會封裝job,進行執行

當我們從文字檔案載入過來,lines就不能改變了

一個RDD由不同的parations組成。spark會並行處理它們。

1.parallelize()方法
把一個存在的集合傳遞給SparkContext
val rdd = sc.parallelize(Array(1,2,2,4),4) 我們一般測試生成資料的時候這樣用。
第一個引數:待並行化處理的集合
第二個引數:分割槽個數
rdd.count() 統計個數
rdd.foreach(print) 遍歷
2.textFlile()方法
載入檔案

scala基礎格式
1.建立變數
val 不可以修改
var 還可以指向型別相同的值

2.匿名函式和型別推斷
lines.filter(line =>line.contains(“world”))
包含world的行
型別推斷:
line可以自己推斷,所以上面沒有指明型別

Transformation 轉換
轉換,從之前的RDD構建一個新的RDD,像map 和 filter
map() 接受函式 ,把函式應用到RDD的每一個元素,返回新的RDD。
filter() 函式 接受一個函式,只包含滿足filter函式的新的RDD,看上面就知道。
flatMap() 對每個輸入元素,輸出多個輸出元素。
distinct 去重
union 兩個RDD取並集
intersection 兩個RDD取交集
substract取差集

Action
在RDD上計算出來一個結果,把結果返回給driver progrom 或者儲存,
1.reduce 最重要
接受一個函式,作用在RDD兩個相同的元素上,返回新的元素。
累加,計數,和其它聚集操作
rdd .reduce((x,y)=>x+y)
累加所有元素

2.collect 遍歷整個RDD,返回內容需要單機記憶體能夠容納下。
大資料時候 saveAsTextFile()這樣一個Action操作

3.take
返回RDD的N個元素,並且嘗試返回最小的分割槽,返回結果無需的。隨機的

4.top
排序(根據RDD中資料的比較器)
5.foreach
計算RDD的每個元素,但是不返回到本地。
可以配合println友好的列印出資料。

RDDs的特性
spark維護者RDD之間的依賴和建立關係。叫做血統關係圖,來計算每個RDD的需求和恢復丟失的資料。
延遲計算
spark對RDD計算是第一次使用action操作的時候。
它可以減少資料傳輸。
載入資料也是延遲計算,資料在必要的時候,才會被載入進去。

persist
預設每次RDD時,為了防止重複計算,我們可以對RDD進行快取,快取也有級別,看上面。

unpersist從快取中移除

keyValue對RDDs:
1.建立keyValue對RDDs
使用map函式,返回key/value對。
加入我們將每行第一個單詞作為key
val rdd = sc.textFile(XXX)
val rdd2 = rdd.map(line=>(line.split(" ")(0),line))
然後這裡rdd2就是一個keyvalue對rdds.
測試的時候:
sc.parallelize(Array((1,2),(3,4)))

2.常見的操作
2.1.reduceByKey
把相同的key結合,比如求和。
2.2groupByKey 把相同key的values分組
2.3combineBykey

3.對transformations
3.1 mapValues() 作用於每個元素,key不變
3.2 flatMapValues() 符號化的時候使用
3.3 keys 僅返回keys
3.4 values 僅返回values
3.5 sortByKey 按照key排序的RDD

combinebyKey()
這個重要
最常用的基於key的聚合函式,返回的型別可以與輸入型別不一樣,groupbykey底層也用了它。

遍歷分割槽中的元素,這個key要不之前見過,要不不是。
如果是新元素,使用createCambiner()
如果是這個partion已經存在的key,就會使用mergeevValue()函式
上面兩步都是一個分割槽的操作,然後使用mergeValue()來整合所有分割槽的結果。

scores.foreach(println)
(jake,80.0)
(jake,90.0)

他有四個引數

相關文章