spark 總結 算子篇

1、創建工程

在這裏添加 spark core包,添加bulid 插件。

<groupId>com.ypl.bigdata</groupId> // 這裏添寫包名稱 
<artifactId>spark-200226</artifactId> // 這裏是項目名稱
<version>1.0-SNAPSHOT</version>//版本號 可以

<dependencies>
     <dependency>
         <groupId>org.apache.spark</groupId>
         <artifactId>spark-core_2.11</artifactId>
         <version>2.1.1</version>
     </dependency>
</dependencies>

<build>
       <finalName>WordCount</finalName>
       <plugins>
           <plugin>
               <groupId>net.alchim31.maven</groupId>
               <artifactId>scala-maven-plugin</artifactId>
               <version>3.2.3</version>
               <executions>
                   <execution>
                       <goals>
                           <goal>compile</goal>
                           <goal>testCompile</goal>
                       </goals>
                   </execution>
               </executions>
           </plugin>
       </plugins>
   </build>


算子總結

map 算子
主要是做數據結構的轉換,數據條數不變。

//創建
scala> var source = sc.parallelize(1 to 10)
// 打印
scala> source.collect()
// 將所有元素*2
scala> val mapadd = source.map(_ * 2)
// 打印最終結構
scala> mapadd.collect()

mapPartitions(func)
對分區數據進行轉換。將某一個分區的所有數據拿過來形成一個可迭代的集合要求 返回可迭代的集合。提高效率會使用它。
應用場景:
只對分區內數據進行數據。缺點是不釋放,可能導致oom。
當內存空間較大的時候建議使用 mapPartitions()以提高效率。

val rdd = sc.parallelize(Array(1,2,3,4))
rdd.mapPartitions(x=>x.map(_*2)) // 是每個元素 * 2組成新的rdd
res3.collect

mapPartitionsWithIndex(func)
獲取分區索引,進行操作。

val rdd = sc.parallelize(Array(1,2,3,4))
val indexRdd = rdd.mapPartitionsWithIndex((index,items)=>(items.map((index,_)))) //使每個元素跟所在分區形成一個元組組成一個新的RDD
indexRdd.collect

flatMap
將一個整體拆成一個個 個體。扁平化

val sourceFlat = sc.parallelize(1 to 5)//(1,2,3,4,5)
sourceFlat.collect()
// (1->1, 2->1 ,2 ....5 -> 1,2,3,4,5)
val flatMap = sourceFlat.flatMap(1 to _)
flatMap.collect()
// Array(1,1,2,1,2,3,1,2,3,4,1,2,3,4,5)

glom
將一個分區形成一個數組,形成新的RDD類型時,RDD[Array[T]]
例如:創建一個4個分區的RDD,將每個分區的數據放到一個數組。
求分區的最大值,最小值等等。

val rdd = sc.parallelize(1 to 16 ,4)
//將每個分區的數據放到一個數組並收集到 Driver 端打印
rdd.glom().collect()

groupBy(func)
按照傳入函數的返回值進行分組。相同的key對應的值放入一個迭代器。
例如:將元素模以2的值進行分組。

val rdd =sc.parallelize(1 to 4)
val group = rdd.groupBy(_%2)
group.collect

filter(func)
過濾,返回一個新的RDD,該RDD 由經過的 func 函數計算後返回值爲true 的元素組成。
例如: 創建一個RDD(由字符串組成)過濾出一個新RDD(包含"xiao"字符串)

var sourceFilter = sc.parallelize(Array("zhang1","zhangy2","zhang3","lisi"))
val filter = sourceFilter.filter(_.contains("zhang"))
filter.collect()

sample(withReplacement,fraction,seed)
以指定的隨機種子隨機抽樣出數量爲fraction 的數據,withReplacement 表示抽出的數據是否放回,true 爲有放回的抽樣,false 爲無放回的抽樣,seed 用於指定隨機數生成器的種子

val rdd = sc.parallelize(1 to 10)
rdd.collect()
var sample1 = rdd.sample(true,0.4,2)
sample1.collect()
// 不放回抽樣
var sample2 = rdd.sample(false,0.2,3)
sample2.collect

disitnct([numPartitions])
對源RDD進行去重後返回一個新的RDD.

distinct task 是4/4 map 的task 是 2/2
首先是 write 任務-> 然後 read 任務
shuffer 會慢,然後有IO 的過程

val distinctRdd = sc.parallelize()
// 不指定並行度
val unionRDD = distinctRdd.distinct()
//打印去重後生成新的RDD
unionRDD.collect()
// 指定並行度
val unionRDD = distinctRdd.distinct(2)

coalesce(numPartitions)
縮減分區數,用於大數據集過濾後,提高小數據集的執行效率

val rdd = sc.parallelize(1 to 16 ,4)
// 查看rdd 分區數
rdd.partitions.size
// 對 RDD 重新分區
val coalesceRDD = rdd.coalesce(3)
// 查看rdd 分區數
coalesceRDD.partitions.size

repartition(numPartition)
根據分區數,重新通過網絡隨機洗牌所有數據
例如: 創建一個 4個分區的RDD,對其重新分區。

val rdd = sc.parallelize(1 to 16 ,4)
rdd.partitions.size
rdd.repartition(2)
rdd.partitions.size
rdd.glom.collect

coalesce 和repartition 的區別,coalesce 重新分區,可以選擇是否 shuffle 過程,由參數 shuffle:Boolean = false /true 決定。
repartition 實際上是調用的coalesce ,默認是進行shuffle。

sortBy()
對數據進行排序

val rdd = sc.parallelize(List(2,1,3,5))
rdd.sortBy(x=>x).collect() // 降序
rdd.sortBy(x=>x ,false).collect() // 升序

雙value類型交互

union(otherDataset)
對源RDD 和參數 RDD 求並集後返回一個新的RDD

val rdd1 = sc.parallelize(1 to 5)
val rdd2 = sc.parallelize(5 to 10)
val rdd3 = rdd1.union(rdd2)
rdd3.collect()

subtract(otherDataset) 案例
計算差的一種函數,去除兩個RDD中相同的元素,不同的RDD 將保留下來

val rdd1 = sc.parallelize(3 to 8)
val rdd2 = sc.parallelize(1 to 5)
val rdd3 = rdd1.substract(rdd1).collect()

intersection(otherDataset) 案例
計算交集的一種函數,保留兩個rdd 相同的元素

val rdd1 = sc.parallelize(1 to 7)
val rdd2 = sc.parallelize(5 to 10)
val rdd3 = rdd1.intersection(rdd2)

cartesian(otherDataset)
兩個rdd 的笛卡爾積

val rdd1 = sc.parallelize(1 to 3 )
val rdd2 = sc.parallelize(2 to 5)
val rdd3 = rdd1.cartesian(rdd2).collect()

zip(otherDataset) 案例
對兩個rdd 進行拉鍊操作,保證分區數和分區數裏的數據量相同。

val rdd1 = sc.parallelize(Array(1,2,3),3 )
val rdd2 = sc.parallelize(Array("a","b","c"),3 )
val rdd3 = rdd1.zip(rdd2).collect

Key-Value類型交互

partitionBy
對pairRDD 進行分區操作,如果原有的partitionRDD和現有的partitionRDD是一致的的話,就不進行分區,否則生成shffuleRDD,即產生shuffle 過程。
例如: 創建一個有四個分區的rdd,對其重新分區。

val rdd1 = sc.parallelize(Array((1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")),4 )
rdd.partitions.size
var rdd2 = rdd.partitionBy(new org.aparche.spark.HashPartitioner(2))

// 自定義分區規則
val config:SparkConf = new SparkConf().setMaster("local[*]").setAppName("WordCount")
// 創建Spark 上下文
val sc = newSpakrContext(config)
//
val listRDD = sc.makeRDD(List(("a",1),("b",2),("c",3)))

val partRDD = listRDD.patitionBy(new MyPartitioner(3))

partRDD.saveAsTextFile("output")

// 聲明分區器
// 繼承Partitioner 類
class MyPartitioner(partitions: Int) extends Partitioner{
	override def numPartitions: Int = {
		pattitions
	}
	override def getPartition(key:Any):Int = {
		1
	}
}


groupByKey
groupByKey 也是對每個key進行操作,但只生成一個 sequence。
創建一個pairRDD,將相同key值聚合到一個sequence中並計算相同key對應值的相加的結果。

val words = Array("1","2","2","3","3","3")
val wordPairsRDD = sc.parallelize(words).map(word => (word,1))
val group = wordPairsRDD.groupByKey()
group.collect()
group.map(t=> (t._1,t._2.sum)).collect

reduceByKey(func,[numTasks]) 案例
在一個(k,v)的rdd 上調用,返回一個(k,v)的rdd,使用指定的 reduce函數,將相同的key的值聚合到一起,reduce任務的個數可以通過第二個可選的參數來設置。
例如:創建一個pairRDD 計算相同key對應值的相加的結果。

val rdd = sc.parallelize(List(("female",1),("male",5),("female",5),("male",2)))
val reduce = rdd.reduceByKey((x,y=> x + y))
reduce.collect()

reduceByKey 和 groupByKey 的區別:
區別
前者是按照key進行聚合在shuffle之前有combine(預聚合)操作,返回結果是RDD[k,v]。
按照key 進行分組,直接進行shuffle。
reduceByKey 比 groupByKey 用多,也要注意業務邏輯上的區別。

aggregateByKey 案例
參數:(zeroValue:U,[partitioner:Partitioner])(seqOp:(U,V)=>U,combOp:(U,U)=>U)
在 kv對的rdd 中,按 key將 value 進行分組合並,合併時,將每個value和初始值作爲seq函數的參數進行計算,返回的結果作爲一個新的kv對,然後將結果按照 key 進行合併,最後將每個分組的value傳遞給combine函數進行計算(先將前兩個value進行計算,將返回結果和下一個value 傳給combine 函數,以此類推),將key與計算結果作爲一個新的kv對 輸出。

zeroValue:給每一個分區中的每一個key一個初始值。
seqOp:分區內運算規則
combOp:分區間運算規則
例如:創建一個pairRDD,取出每個分區相同key 對應值的最大值,然後相加。

// 創建parirdd
val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
// 查看分區情況
rdd.glom.collect
val agg = rdd.aggregateByKey(0)(math.max(_,_),_+_)
agg.collect

在這裏插入圖片描述

拓展:

val agg = rdd.aggregateByKey(10)(math.max(_._),_+_).collect
Array((a,10),(b,10),(c,20))

val agg = rdd.aggregateByKey(10)(_+_,_+_).collect

foldByKey
底層代碼和 aggregateByKey 都調用了一個方法,,seqop和combop相同。

// 創建parirdd
val rdd = sc.parallelize(List(("a",3),("a",2),("c",4),("b",3),("c",6),("c",8)),2)
// 查看分區情況
rdd.glom.collect
val agg = rdd.foldByKey(0)(_+_)
agg.collect

combineByKey[C] 案例
參數:(createCombine:V=>C,mergeValue:(C,V)=>C,mergeCombiners:(C,C)=>C)
對於相同的K,把 V 合併成一個集合

  1. createCombiner:combineByKey() 會遍歷分區中的所有元素,因此每個元素的鍵會被初始化,創建對於累加器的初始值。
    2)mergeValue:對之前分區內處理的鍵進行累加,和新的值進行合併。
    3)mergeCombiners: 對分區間的數據進行累加。
    在這裏插入圖片描述
val input = sc.parallelize(Array(("a",88),("b",95),("a",91),("b",93),("a",95),("b",98)),2) 
val combine = input.combineByKey(
	(_,1),
	(acc:(Int,Int),v)=>(acc._1+v,acc._2+1),
	(acc1:(Int,Int),acc2:(Int,Int))=>(acc1._1+acc2._1,acc1._2+acc2._2,))
	)
combine.collect
val result = combine.map{case(key,value)=>(key,value._1/value._2.toDouble)}
result.collect()

sortByKey([ascending],[numTasks]) 案例
作用在一個(k,v)的 RDD 上調用,K必須實現Order 接口,返回一個按照 key進行排序的(k,v) 的 RDD.

val rdd = sc.parallelize(Array((3,"aa"),(6,"cc"),(2,"bb"),(1,"dd")))
rdd.sortBykey(true).collect()
rdd.sortBykey(false).collect()

mapValues 案例
針對(k,v)形式的類型只對V 進行操作
例如創建一個pairRDD,並將value 添加字符串 “||||”

val rdd3 = sc.parallelize(Array(1,"a"),(1,"d"),(2,"b"),(3,"c"))
rdd3.mapValues(_+"|||").collect()

join(otherDataset,[numTasks]) 案例
在類型(k,v) 和 (k,w) 的RDD 上調用,返回一個相同key 對應的所有元素堆在一起的(k,(v,w))的 RDD
例如:創建兩個pariRDD,並將相同的數據聚合到一個元組。

val rdd1 = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
val rdd2 = sc.parallelize(Array((1,3),(2,4),(3,9)))
rdd1.join(rdd2).collect()
Array[(Int,(String,Int))] = Array((2,(b,5)),(1,(a,4)),(3,(c,6)))

cogroup 案例

在類型爲(k,v)和(k ,w )的 rdd 上調用,返回一個(l,(Iterable,Iterable))類型的RDD
創建兩個pairRDD,將key相同的數據聚合到一個迭代器。

val rdd1 = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
val rdd2 = sc.parallelize(Array((1,3),(2,4),(3,9)))
rdd1.cogroup(rdd1).collect()

cogroup 可以把不是共同存在的元素,展示出來,join 不行。

案例,每個省份的廣告統計。
樣例:

package com.ypl.bigdata.spark

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object Practice {

  def main(args: Array[String]): Unit = {
    
    // 1初始化spark 配置信息建立與spark的鏈接

    val sparkconf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("Test")
    
    val sc = new SparkContext(sparkconf)
    
    
    // 2、讀取數據生成RDD : TS, Province,City,User,AD
    val line = sc.textFile("xxx/agent.log")


    // 3、按照最小力度進行聚合;((Province,AD),1)
    val proAdANDOne: RDD[((String, String), Int)] = line.map(x => {
      val fields: Array[String] = x.split(" ")
      ((fields(1), fields(4)), 1)
    }
    )


    // 4、 計算每個廣告點擊的總數 ;((Province,AD),sum)
    val proAdANDsum: RDD[((String, String), Int)] = proAdANDOne.reduceByKey(_ + _)
    
    // 5、 將省份作爲 key,廣告+ 點擊次數爲 value (province,(AD,sum))

    val proANDAdsum: RDD[(String, (String, Int))] = proAdANDsum.map(x => (x._1._1, (x._1._2, x._2)))

    // 6、將同一省的廣告進行聚合
    val provinceGroup: RDD[(String, Iterable[(String, Int)])] = proANDAdsum.groupByKey()

    // 7 排序、取前三條、打印
    val provinceAdTop3: RDD[(String, List[(String, Int)])] = provinceGroup.mapValues(
      x => x.toList.sortWith((x, y) => x._2 > y._2).take(3)
    )

    provinceAdTop3.collect().foreach(println)
    
    // 關閉連接
    sc.stop()
    
  }
}

Action

reduce
通過func 函數聚集的RDD 中所有元素,先聚合分區內數據,再聚合分區間數據。
例:聚合RDD[Int] 、RDD[String] 中的所有元素

val rdd1 = sc.makeRDD(1 to 10 ,2)
rdd1.reduce(_+_)

val rdd1 = sc.makeRDD(Array(("a",1),("a",3),("c",3),("d",5)))
rdd2.reduce((x,y=>(x._1 + y._1, x._2+y._2)))
(adca,12

collect()
在驅動程序中,以數組的形式返回數據集中的所有元素

count()
返回rdd 中元素的個數。

first()
返回 rdd 第一個元素

take(n)
取出rdd中第第幾個元素

takeOrdered(3)
排序完後取出第幾個元素

aggregare()
創建一個rdd ,將所有元素相加

val rdd1 = sc.makeRDD(1 to 10,2)
rdd1.aggregate(0)(_+_,_+_) //55
rdd1.aggregate(10)(_+_,_+_) // 85 2 個分區 10 + 分區間計算 10

fold(num)(func) 案例
摺疊操作,aggregare 的簡化操作,seqop和combop 一樣

val rdd1 = sc.makeRDD(1 to 10,2)
rdd.fold(0)(_+_)

saveAsTextFile(path)
將數據集的元素以textfile的形式保存到HDFS文件系統或者其支持的文件系統,對於每個元素,Spark 將會調用 toString方法,將它轉換爲文件中的文本。

saveAsSequenceFile(path)
將數據集中的元素以Hadoop sequencefile 的格式保存到指定目錄下,可以使HDFS或者其他Hadoop 支持的文件系統。

saveAsObjectFile(path)
將RDD 中的元素序列化對象,存儲到文件當中。

countByKey() 案例
針對(k,v) 類型的rdd 返回一個(k,Int) 的map ,表示每一個key對應的元素個數。

val rdd1 = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
rdd.countByKey

foreach(func) 案例
在數據集的每一個元素上,運行函數func 進行更新

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