使用Spark rdd 開發spark程序

1.常用的rdd

函數 說明
map(func) 返回一個新的分佈式數據集,由每個原元素經過func函數轉換後組成
filter(func) 返回一個新的數據集,由經過func函數後返回值爲true的原元素組成
flatMap(func) 類似於map,但是每一個輸入元素,會被映射爲0到多個輸出元素(因此,func函數的返回值是一個Seq,而不是單一元素)
sample(withReplacement, frac, seed) 根據給定的隨機種子seed,隨機抽樣出數量爲frac的數據
union(otherDataset) 返回一個新的數據集,由原數據集和參數聯合而成
groupByKey([numTasks]) 在一個由(K,V)對組成的數據集上調用,返回一個(K,Seq[V])對的數據集。注意:默認情況下,使用8個並行任務進行分組,你可以傳入numTask可選參數,根據數據量設置不同數目的Task
reduceByKey(func, [numTasks]) 在一個(K,V)對的數據集上使用,返回一個(K,V)對的數據集,key相同的值,都被使用指定的reduce函數聚合到一起。和groupbykey類似,任務的個數是可以通過第二個可選參數來配置的。
join(otherDataset, [numTasks]) 在類型爲(K,V)和(K,W)類型的數據集上調用,返回一個(K,(V,W))對,每個key中的所有元素都在一起的數據集
reduce(func) 通過函數func聚集數據集中的所有元素。Func函數接受2個參數,返回一個值。這個函數必須是關聯性的,確保可以被正確的併發執行
collect() 在Driver的程序中,以數組的形式,返回數據集的所有元素。這通常會在使用filter或者其它操作後,返回一個足夠小的數據子集再使用,直接將整個RDD集Collect返回,很可能會讓Driver程序OOM
count() 返回數據集的元素個數
take(n) 返回一個數組,由數據集的前n個元素組成。注意,這個操作目前並非在多個節點上,並行執行,而是Driver程序所在機器,單機計算所有的元素(Gateway的內存壓力會增大)
first() 返回數據集的第一個元素(類似於take(1))
saveAsTextFile(path) 將數據集的元素,以textfile的形式,保存到本地文件系統,hdfs或者任何其它hadoop支持的文件系統。Spark將會調用每個元素的toString方法,並將它轉換爲文件中的一行文本
saveAsSequenceFile(path) 將數據集的元素,以sequencefile的格式,保存到指定的目錄下,本地系統,hdfs或者任何其它hadoop支持的文件系統。RDD的元素必須由key-value對組成,並都實現了Hadoop的Writable接口,或隱式可以轉換爲Writable(Spark包括了基本類型的轉換,例如Int,Double,String等等)
foreach(func) 在數據集的每一個元素上,運行函數func。這通常用於更新一個累加器變量,或者和外部存儲系統做交互

rdd的輸入和輸出,scala版,java版

scala版本:
https://github.com/zhouyang-bigdata/SparkCommonRdd

java版本:
https://github.com/zhouyang-bigdata/SparkCommonRddJava

每個rdd函數的使用場景

spark 默認提供的rdd多達幾十個,其中有與外部連接相關的,比如kafkaRdd,也有專門處理數據的。這裏介紹的是專門處理數據的。
spark rdd適合的場景有:

  1. 轉換
  2. 過濾
  3. 計數
  4. 排序
  5. 分類
  6. 統計

轉換
Map,最有用的轉換函數。map() 接收一個函數,把這個函數用於 RDD 中的每個元素,將函數的返回結果作爲結果RDD
mapPartition可以倒過來理解,先partition,再把每個partition進行map函數
map,mapToPair:一對一轉換。
flatMap,flatMapToPair:一對多轉換。

過濾
filter,這通常用於規則校驗,條件查詢中。條件查詢通常有:等於、小於、大於、小於等於、大於等於、模糊匹配,區間等。對於複雜條件,涉及到多個實體對象,需要注意序列化問題。

計數
reduceByKey,reduce,count,accumulator
計數有2種,一種是對值本身累加,一種是對值的數量計數。
reduceByKey接收一個函數,按照相同的key進行reduce操作。
累加器也可以作爲計數工具,累加器本身用處是統計程序執行次數,做一些處理,也可以做對值的數量計數。

排序
sortByKey
用於對pairRDD按照key進行排序,第一個參數可以設置true或者false,默認是true。true是升序。
這裏有個問題,就是爲什麼沒有sort。也就是對非鍵值對排序,沒有對應的rdd。實際上,可以通過其它rdd去實現。

分類
groupByKey
Cogroup
Reduce
Countbykey
groupByKey會將RDD[key,value] 按照相同的key進行分組,形成RDD[key,Iterable[value]]的形式
cogroup
groupByKey是對單個 RDD 的數據進行分組,還可以使用一個叫作 cogroup() 的函數對多個共享同一個鍵的 RDD 進行分組 。
分類通常以某個對象爲依據。按這個概念,它的用途非常廣泛。

統計
統計比較複雜,幾乎沒有算子單獨實現需求。常見的統計有:max,min,avg,sum,趨勢。

實際的例子

  1. 條件查詢
    查詢包含等於、小於、大於、小於等於、大於等於、模糊匹配,區間等條件的數據。
    使用map,filter。
  2. 求最值
    求max,min,avg,sum等數據。
    具體可參見:
    https://github.com/zhouyang-bigdata/SparkRddRealExamples

特殊rdd的使用選擇

特殊rdd,對筆者而言,比較特別的rdd跟以下幾個方面有聯繫:

  1. shuffle操作
  2. 在driver上還是在executor上執行
  3. partition的意義
  4. Union,join,aggregate

(1)shuffle操作
我們知道,reducebykey,groupbykey是會發生shuffle操作的。事實上,repartition,join,cogroup,和任何的By或ByKey轉換可以導致洗牌。 Shuffle操作,會把轉換的中間數據進行分割,然後臨時存到磁盤。直到下一次轉換,需要從磁盤讀取數據。這個操作很低效,而且帶來大量內存消耗。爲什麼會消耗大量內存呢?因爲,對於map,filter等操作,從磁盤讀取數據也好,還是處理上一步操作的中間數據,數據本身是一個抽象的rdd數據集,數據是以流的形式,小批次的放入內存,在一個時間點,內存消耗並不是很高。而這裏的讀磁盤數據,是一次性的讀取大量數據到內存,內存消耗大。
除此之外,這2個操作,都是寬依賴,同時會產生網絡io,io大小與數據量和partition的數量有關。

(2)在driver上還是在executor上執行
這是一個容易犯錯的問題。比如foreachRDD,是在driver端執行。foreach,foreachPartition在executor上執行。對於面向外部連接,比如mysql連接,redis連接,hbase連接,我們不想要都在driver端創建,因爲這樣,每個executor端都從driver端拿數據。這裏面有個情況容易混淆,就是:
數據是都在driver端處理還是僅彙總到driver端?
這個區別,直接將Action算子分爲2類。

(3)partition的意義
Partition的思路,是把數據分成一塊一塊的,再使用task並行計算。一般推薦每個真核分2-4partition。Partition與task的關係?
可參見:
https://blog.csdn.net/cafebar123/article/details/79684596

我們知道mappartition在處理外部連接的時候,比map更有效,比如mysql,redis。
使用map與mapPartition,本質區別,是每次載入內存的數據的大小,越大,則函數執行次數越少,綜合來說,執行次數越少越好,原因有二:

  1. 函數每執行一次,jvm就需分析一次;次數越少,jvm分析的總時間就越少。

  2. 對於建立外部連接時,比如讀取mysql,kafka數據,建立的連接越少越高效。
    但每次載入內存的數據大小,也不能過大。個人建議應不大於每個task分配的內存的1/3。

reduceByKey,groupByKey,的使用選擇

collect,count的使用選擇

總結

在這裏插入圖片描述

在這裏插入圖片描述

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