SparkContext的创建
#创建一个SparkConf
val work: SparkConf = new >SparkConf().setMaster(“local[*]”).setAppName(“work”)
#创建一个SparkContext
val sc = new SparkContext(work)
Spark RDD创建
- 创建一个RDD(有如下几种方式)
val rdd = sc.parallelize(Arral(1,2,3,4,5,6,7,8))
val rdd = sc.makeRDD(Array(1,2,3,4,5,6,7,8))
val rdd = sc…textFile(“scr/hotel.txt”)
转换算子
- map算子
- flatMap算子
- mapPartitions算子
- mapPartitionsWithlndex算子
- union算子
- cartesian算子
- groupBy算子、groupByKey算子
- filter算子
- distinct算子
- subtract算子
- sample算子
- cache算子
- persist算子
- mapValues算子、flatMapValues算子
- combineByKey算子
- reduceByKey算子
- repartition算子
- cogroup算子
- join算子
- leftOutJoin和rightOutJoin算子、fullOuterJoin算子
- intersection算子
- foreach算子
- saveAsTextFile算子
- saveAsObjectFile算子
- collect算子
- collectAsMap算子
- count,countByKey,CountByValue算子
- take、takeSample算子
- reduce算子
- aggregate算子
- zip、zipWithlndex算子(转换算子)
Value数据类型的Transformation算子
- 输入分区与输出分区一对一型
- map算子
处理数据是一对一的关系,进入一条数据,出去的还是一条数据。map的输入变换函数应用于RDD中所有的元素,而mapPartitions应用于所有分区。区别于mapPartitions主要在于调度粒度不同。如parallelize(1 to 10 ,3),map函数执行了10次,而mapPartitions函数执行了3次。
val infos: RDD[String] = sc.parallelize(Array[String]("hello spark","hello hdfs","hello HBase"))
val result: RDD[Array[String]] = infos.map(one => {
one.split(" ")
})
result.foreach(arr =>{arr.foreach(println)})
- flatMap算子
flatMap是一对多的关系,处理一条数据得到多条结果,将原来 RDD 中的每个元素通过函数 f 转换为新的元素,并将生成的 RDD 的每个集合中的元素合并为一个集合。
val infos: RDD[String] = sc.makeRDD(Array[String]("hello spark","hello hdfs","hello MapReduce"))
val rdd1: RDD[String] = infos.flatMap(one => {
one.split(" ")
})
rdd1.foreach(println)
- mapPartitions算子
mapPartitions遍历的是每一个分区中的数据,一个个分区的遍历。获取到每个分区的迭代器,在函数中通过这个分区整体的迭代 器对整 个分区的元素进行操作,相对于map一条条处理数据,性能比较高,可获取返回值。
可以通过函数f(iter) =>iter.filter(_>=3)对分区中所有的数据进行过滤,大于和等于3的数据保留,一个方块代表一个RDD分区,含有1,2,3的分区过滤,只剩下元素3。
val listRDD: RDD[Int] = sc.makeRDD(List(1,2,3,4,5,6,5,4,3,2,1), 2)
val mappartitionsRDD: RDD[Int] = listRDD.mapPartitions(x => {
x.filter(_ > 3)
})
mappartitionsRDD.foreach(println(_))
- mapPartitionsWithlndex算子
拿到每个RDD中的分区,以及分区中的数据
val lines: RDD[String] = sc.textFile("./data/words",5)
val result: RDD[String] = lines.mapPartitionsWithIndex((index, iter) => {
val arr: ArrayBuffer[String] = ArrayBuffer[String]()
iter.foreach(one => {
// one.split(" ")
arr.append(s"partition = [$index] ,value = $one")
})
arr.iterator
}, true)
result.foreach(println)
- 输入分区与输出分区多对一型
- union算子
union合并两个RDD,两个RDD必须是同种类型,不一定是K,V格式的RDD
val rdd1: RDD[String] = sc.parallelize(List[String]("zhangsan","lisi","wangwu","maliu"),3)
val rdd2: RDD[String] = sc.parallelize(List[String]("a","b","c","d"),4)
val unionRDD: RDD[String] = rdd1.union(rdd2)
unionRDD.foreach(println)
- cartesian算子
求笛卡尔积,该操作不会执行shuffle操作,但最好别用,容易触发OOM
val conf=new SparkConf().setAppName("cratesian").setMaster("local")
val sc=new SparkContext(conf)
val arr1=sc.parallelize(Array(1,3,5))
val arr2=sc.parallelize(Array(2,4,6))
arr1.cartesian(arr2).collect.foreach(println)
- 输出分区与输出分区多对多型
- groupBy算子、groupByKey算子
groupBy算子按照指定的规则,将数据分组
val rdd: RDD[(String, Double)] = sc.parallelize(List[(String,Double)](("zhangsan",66.5),("lisi",33.2),("zhangsan",66.7),("lisi",33.4),("zhangsan",66.8),("wangwu",29.8)))
val result: RDD[(Boolean, Iterable[(String, Double)])] = rdd.groupBy(one => {
one._2 > 34
})
result.foreach(println)
groupByKey算子根据key去将相同的key对应的value合并在一起(K,V)=>(K,[V])
val rdd: RDD[(String, Double)] = sc.parallelize(List[(String,Double)](("zhangsan",66.5),("lisi",33.2),("zhangsan",66.7),("lisi",33.4),("zhangsan",66.8),("wangwu",29.8)))
val rdd1: RDD[(String, Iterable[Double])] = rdd.groupByKey()
rdd1.foreach(info=>{
val name: String = info._1
val value: Iterable[Double] = info._2
val list: List[Double] = info._2.toList
println("name = "+name+",value ="+list)
})
- 输出分区是输入分区子集类型
- filter算子
过滤数据,返回true的数据会被留下
val infos: RDD[String] = sc.makeRDD(List[String]("hehe","hahha","zhangsan","lisi","wangwu"))
val result: RDD[String] = infos.filter(one => {
!one.equals("zhangsan")
})
result.foreach(println)
9. distinct算子
distinct去重,有shuffle产生,内部实际是map+reduceByKey+map实现
val infos: RDD[String] = sc.parallelize(List[String]("a","a","b","a","b","c","c","d"),4)
val result: RDD[String] = infos.distinct()
result.foreach(println)
- subtract算子
取RDD的差集,subtract两个RDD的类型要一致,结果RDD的分区数与subtract算子前面的RDD分区数多的一致。
val rdd1 = sc.parallelize(List[String]("zhangsan","lisi","wangwu"),5)
val rdd2 = sc.parallelize(List[String]("zhangsan","lisi","maliu"),4)
val subtractRDD: RDD[String] = rdd1.subtract(rdd2)
subtractRDD.foreach(println)
println("subtractRDD partition length = "+subtractRDD.getNumPartitions)
11. sample算子
sample随机抽样,参数sample(withReplacement:有无放回抽样,fraction:抽样的比例,seed:用于指定的随机数生成器的种子)
有种子和无种子的区别:
有种子是只要针对数据源一样,都是指定相同的参数,那么每次抽样到的数据都是一样的
没有种子是针对同一个数据源,每次抽样都是随机抽样
val lines: RDD[String] = sc.textFile("./data/sampleData.txt")
val result: RDD[String] = lines.sample(true,0.01,100)
result.foreach(println)
sc.stop
- Cache算子
- cache算子
- persist算子
cache()和persist()注意问题
1.cache()和persist()持久化单位是partition,cache()和persist()是懒执行算子,需要action算子触发执行
2.对一个RDD使用cache或者persist之后可以赋值给一个变量,下次直接使用这个变量就是使用持久化的数据。也可以直接对RDD进行cache或者persist,不赋值给一个变量
3.如果采用第二种方法赋值给变量的话,后面不能紧跟action算子
4.cache()和persist()的数据在当前application执行完成之后会自动清除
val conf: SparkConf = new SparkConf().setMaster("local").setAppName("cache")
val sc: SparkContext = new SparkContext(conf)
sc.setLogLevel("Error")
val lines: RDD[String] = sc.textFile("./data/persistData.txt")
// val linescache: RDD[String] = lines.persist(StorageLevel.MEMORY_ONLY)
val linescache: RDD[String] = lines.cache()
val startTime1: Long = System.currentTimeMillis()
val count1: Long = linescache.count()
val endTime1: Long = System.currentTimeMillis()
println("count1 = "+count1+". time = "+(endTime1-startTime1) + "mm")
val starttime2: Long = System.currentTimeMillis()
val count2: Long = linescache.count()
val endTime2: Long = System.currentTimeMillis()
println("count2 = "+count2+", time = "+(endTime2-starttime2) + "ms")
sc.stop()
Key-Value数据类型的Transformation算子
- 输入分区与输出分区一对一
- mapValues算子、flatMapValues算子
mapValues算子针对K,V格式的数据,只对Value做操作,Key保持不变
val infos: RDD[(String, String)] = sc.makeRDD(
List[(String,String)](
("zhangsan","18"),("lisi","20"),("wangwu","30")
))
val result: RDD[(String, String)] = infos.mapValues(s => {
s + " " + "zhangsan18"
})
result.foreach(println)
sc.stop()
flatMapValues算子(K,V)->(K,V),作用在K,V格式的RDD上,对一个Key的一个Value返回多个Value
- 对单个RDD或者两个RDD聚集
单个RDD聚集
- combineByKey算子
首先给RDD中每个分区中的每一个key一个初始值
其次在RDD每个分区内部相同的key聚合一次
再次在RDD不同的分区之间将相同的key结果聚合一次
val rdd1: RDD[(String, Int)] = sc.makeRDD(List[(String, Int)](
("zhangsan", 10), ("zhangsan", 20), ("wangwu", 30),
("lisi", 40), ("zhangsan", 50), ("lisi", 60),
("wangwu", 70), ("wangwu", 80), ("lisi", 90)
),3)
rdd1.mapPartitionsWithIndex((index,iter)=>{
val arr: ArrayBuffer[(String, Int)] = ArrayBuffer[(String,Int)]()
iter.foreach(tp=>{
arr.append(tp)
println("rdd1 partition index ="+index+".value ="+tp)
})
arr.iterator
}).count()
println("++++++++++++++++++++++++++++++++++++")
val result: RDD[(String, String)] = rdd1.combineByKey(v=>{v+"hello"}, (s:String, v)=>{s+"@"+v}, (s1:String, s2:String)=>{s1+"#"+s2})
result.foreach(println)
- reduceByKey算子
首先会根据key去分组,然后在每一组中将value聚合,作用在KV格式的RDD上
val infos: RDD[(String, Int)] = sc.parallelize(
List[(String,Int)](("zhangsan",1),("zhangsan",2),
("zhangsan",3),("lisi",100),("lisi",200)),5
)
val result: RDD[(String, Int)] = infos.reduceByKey((v1, v2)=>{v1+v2})
result.foreach(println)
sc.stop()
17. repartition算子
重新分区,可以将RDD的分区增多或者减少,会产生shuffle,coalesc(num,true) = repartition(num)
val rdd1: RDD[String] = sc.parallelize(List[String](
"love1", "love2", "love3", "love4",
"love5", "love6", "love7", "love8",
"love9", "love10", "love11", "love12"
), 3)
val rdd2: RDD[String] = rdd1.mapPartitionsWithIndex((index, iter) => {
val list: ListBuffer[String] = ListBuffer[String]()
iter.foreach(one => {
list.append(s"rdd1 partition = [$index] ,value = [$one]")
})
list.iterator
}, true)
val rdd3: RDD[String] = rdd2.repartition(3)
val rdd4: RDD[String] = rdd3.mapPartitionsWithIndex((index, iter) => {
val arr: ArrayBuffer[String] = ArrayBuffer[String]()
iter.foreach(one => {
arr.append(s"rdd3 partition = [$index] ,value = [$one]")
})
arr.iterator
})
val results: Array[String] = rdd4.collect()
results.foreach(println)
sc.stop()
两个RDD聚集
- cogroup算子
合并两个RDD,生成一个新的RDD。分区数与分区数多个那个RDD保持一致
val rdd1 = sc.parallelize(List[(String,String)](("zhangsan","female"),("zhangsan","female1"),("lisi","male"),("wangwu","female"),("maliu","male")),3)
val rdd2 = sc.parallelize(List[(String,Int)](("zhangsan",18),("lisi",19),("lisi",190),("wangwu",20),("tianqi",21)),4)
val resultRDD: RDD[(String, (Iterable[String], Iterable[Int]))] = rdd1.cogroup(rdd2)
resultRDD.foreach(info=>{
val key = info._1
val value1: List[String] = info._2._1.toList
val value2: List[Int] = info._2._2.toList
println("key ="+key+",value"+value1+", value2 = "+value2)
})
println("resultRDD partition length ="+resultRDD.getNumPartitions)
sc.stop()
- 连接
- join算子
会产生shuffle,(K,V)格式的RDD和(K,V)格式的RDD按照相同的K,join得到(K,(V,W))格式的数据,分区数按照大的来。
val nameRDD: RDD[(String, String)] = sc.parallelize(List[(String,String)](("zhangsan","female"),("lisi","male"),("wangwu","female")),3)
val scoreRDD: RDD[(String, Int)] = sc.parallelize(List[(String,Int)](("zhangsan",18),("lisi",19),("wangwu",20)),2)
val joinRDD: RDD[(String, (String, Int))] = nameRDD.join(scoreRDD)
println(joinRDD.getNumPartitions)
joinRDD.foreach(println)
20. leftOutJoin和rightOutJoin算子、fullOuterJoin算子
leftOuterJoin(K,V)格式的RDD和(K,V)格式的RDD,使用leftOuterJoin结合,以左边的RDD出现的key为主 ,得到(K,(V,Option(W)))
val nameRDD: RDD[(String, String)] = sc.parallelize(
List[(String,String)](("zhangsan","female"),
("lisi","male"),("wangwu","female"),("maliu","male")
))
val scoreRDD: RDD[(String, Int)] = sc.parallelize(
List[(String,Int)](("zhangsan",22),("lisi",19),
("wangwu",20),("tianqi",21)
))
val leftOutJoin: RDD[(String, (String, Option[Int]))] = nameRDD.leftOuterJoin(scoreRDD)
leftOutJoin.foreach(println)
sc.stop()
rightOuterJoin(K,V)格式的RDD和(K,W)格式的RDD使用rightOuterJoin结合以右边的RDD出现的key为主,得到(K,(Option(V),W))
val nameRDD: RDD[(String, String)] = sc.parallelize(
List[(String,String)](("zhangsan","female"),("lisi","male")
,("wangwu","female"),("maliu","male")),3
)
val scoreRDD: RDD[(String, Int)] = sc.parallelize(
List[(String,Int)](("zhangsan",18),("lisi",19),
("wangwu",20),("tianqi",21)),4
)
val rightOuterJoin: RDD[(String, (Option[String], Int))] = nameRDD.rightOuterJoin(scoreRDD)
rightOuterJoin.foreach(println)
println("rightOuterJoin RDD partiotion length = "+rightOuterJoin.getNumPartitions)
sc.stop()
fullOuterJoin算子(K,V)格式的RDD和(K,V)格式的RDD,使用fullOuterJoin结合是以两边的RDD出现的key为主,得到(K(Option(V),Option(W)))
val nameRDD: RDD[(String, String)] = sc.parallelize(List[(String,String)](("zhangsan","female"),("lisi","male"),("wangwu","female"),("maliu","male")),3)
val ageRDD: RDD[(String, Int)] = sc.parallelize(List[(String,Int)](("zhangsan",18),("lisi",16),("wangwu",20),("tianqi",21)),4)
val fullOuterJoin: RDD[(String, (Option[String], Option[Int]))] = nameRDD.fullOuterJoin(ageRDD)
fullOuterJoin.foreach(println)
println("fullOuterJoin RDD partition length = "+fullOuterJoin.getNumPartitions)
sc.stop()
- intersection算子
intersection取两个RDD的交集,两个RDD的类型要一致,结果RDD的分区数要与两个父RDD多的那个一致
val rdd1: RDD[String] = sc.parallelize(List[String]("zhangsan","lisi","wangwu"),5)
val rdd2: RDD[String] = sc.parallelize(List[String]("zhangsan","lisi","maliu"),4)
val result: RDD[String] = rdd1.intersection(rdd2)
result.foreach(println)
println("intersection partition length = "+ result.getNumPartitions)
sc.stop()
行动算子
Action算子
- 无输出
- foreach算子
foreach遍历RDD中的每一个元素
val lines: RDD[String] = sc.textFile("./data/words") lines.foreach(println)
- HDFS
- saveAsTextFile算子
将DataSet中的元素以文本的形式写入本地文件系统或者HDFS中,Spark将会对每个元素调用toString方法,将数据元素转换成文本文件中的一行数据,若将文件保存在本地文件系统,那么只会保存在executor所在机器的本地目录
val infos: RDD[String] = sc.parallelize(List[String]("a","b","c","e","f","g"),4)
infos.saveAsTextFile("./data/infos")
- saveAsObjectFile算子
将数据集中元素以ObjectFile形式写入本地文件系统或者HDFS中
infos.saveAsObjectFile("./data/infosObject")
- Scala集合和数据类型
- collect算子
collect回收算子,会将结果回收到Driver端,如果结果比较大,就不要回收,这样的话会造成Driver端的OOM
val lines: RDD[String] = sc.textFile("./data/words")
sc.setLogLevel("Error")
val result: Array[String] = lines.collect()
result.foreach(println)
- collectAsMap算子
将K、V格式的RDD回收到Driver端作为Map使用
val weightInfos: RDD[(String, Double)] = sc.parallelize(
List[(String,Double)](new Tuple2("zhangsan",99),
new Tuple2("lisi",78.6),
new Tuple2("wangwu",122.2323)
)
)
val stringToDouble: collection.Map[String, Double] = weightInfos.collectAsMap()
stringToDouble.foreach(tp=>{
println(tp._1+"**************"+tp._2)
})
sc.stop()
- count,countByKey,CountByValue算子
count统计RDD共有多少行数据
val lines: RDD[String] = sc.textFile("./data/sampleData.txt")
val result: Long = lines.count()
println(result)
sc.stop()
直接给出结果行数
countByKey统计相同的key出现的个数
val rdd: RDD[(String, Integer)] = sc.makeRDD(List[(String,Integer)](
("a",1),("a",100),("a",1000),("b",2),("b",200),("c",3),("c",4),("d",122)
))
val result: collection.Map[String, Long] = rdd.countByKey()
result.foreach(println)
countByValue统计RDD中相同的Value出现的次数,不要求数据必须为RDD格式
val rdd = sc.makeRDD(List[(String,Integer)](
("a",1),("a",1),("a",1000),("b",2),("b",200),("c",3),("c",3)
))
val result: collection.Map[(String, Integer), Long] = rdd.countByValue()
result.foreach(println)
- take、takeSample算子
take取出RDD中的前N个元素
val lines: RDD[String] = sc.textFile("./data/words")
val array: Array[String] = lines.take(3)
array.foreach(println)
takeSapmle(withReplacement,num,seed),随机抽样将数据结果拿回Driver端使用,返回Array,
withReplacement:有无放回抽样,num:抽样的条数,seed:种子
val lines: RDD[String] = sc.textFile("./data/words")
val result: Array[String] = lines.takeSample(false,3,10)
result.foreach(println)
- reduce算子
val rdd: RDD[Int] = sc.makeRDD(Array[Int](1,2,3,4,5))
val result: Int = rdd.reduce((v1, v2) => { v1 + v2 })
//直接得到结果
println(result) }
- aggregate算子
首先是给定RDD的每一个分区一个初始值,然后RDD中每一个分区中按照相同的key,结合初始值去合并,最后RDD之间相同的key聚合
val rdd1: RDD[(String, Int)] = sc.makeRDD(List[(String, Int)](
("zhangsan", 10), ("zhangsan", 20), ("wangwu", 30),
("lisi", 40), ("zhangsan", 50), ("lisi", 60),
("wangwu", 70), ("wangwu", 80), ("lisi", 90)
), 3)
rdd1.mapPartitionsWithIndex((index,iter)=>{
val arr: ArrayBuffer[(String, Int)] = ArrayBuffer[(String,Int)]()
iter.foreach(tp=>{
arr.append(tp)
println("rdd1 partition index ="+index+", value ="+tp)
})
arr.iterator
}).count()
val result: RDD[(String, String)] =
rdd1.aggregateByKey("hello")(
(s, v)=>{s+"~"+v}, (s1, s2)=>{s1+"#"+s2}
)
result.foreach(println)
打印结果
mapPartitionsWithIndex注释掉执行结果:
- zip、zipWithlndex算子(转换算子)
zip算子 —Transformation类算子,将两个RDD合成一个K,V格式的RDD,分区数要相同,每个分区中的元素必须相同
val rdd1: RDD[String] = sc.parallelize(List[String]("a","b","c","d"),2)
val rdd2: RDD[Int] = sc.parallelize(List[Int](1,2,3,4),2)
val result: RDD[(String, Int)] = rdd1.zip(rdd2)
result.foreach(println)
打印结果
zipWithIndex算子—Transformation类算子
val rdd1 = sc.parallelize(List[String]("a","b","c"),2)
val rdd2 = sc.parallelize(List[Int](1,2,3),numSlices = 2)
val result: RDD[(String, Long)] = rdd1.zipWithIndex()
val result2: RDD[(Int, Long)] = rdd2.zipWithIndex()
result.foreach(println)
result2.foreach(println)
打印结果
Spark内置函数RDD Spark中有收藏–Spark RDD的函数详解
将程序打包成jar包在集群上运行
pass
List匹配
//partition:按照某种条件对数据进行分类
//def partition(p: A => Boolean): (Repr, Repr)
println(List(1, 2, 3, 4, 5) partition (_ % 2 == 0))
//(List(2, 4),List(1, 3, 5))
//find:查找集合第一个符合条件的元素
//def find(p: A => Boolean): Option[A]
println(List(1, 2, 3, 4, 5) find (_ % 2 == 0)) //Some(2)
println(List(1, 2, 3, 4, 5) find (_ <= 0)) //None
//takeWhile:着个匹配符合条件的元素 直到不符合条件 之后的元素不在判断
//override def takeWhile(p: A => Boolean): List[A]
println(List(6, 5, 2, 3, 5) takeWhile (_ < 4))
//List()
//dropWhile:着个匹配去除符合条件的元素 直到不符合条件 之后的元素不在判断
//override def dropWhile(p: A => Boolean): List[A]
println(List(1, 2, 6, 4, 5) dropWhile (_ < 4))
//List(6, 4, 5)
//span 着个匹配按照某种条件对数据进行分类 直到不符合条件 之后的元素不在判断
//override def span(p: A => Boolean): (List[A], List[A])
println(List(1, 7, 3, 4, 5) span (_ < 4))
//(List(1),List(7, 3, 4, 5))
//forall 当所有的元素满足条件时 返回true 否则返回false
//def forall(p: A => Boolean): Boolean
println(List(1, 7, 3, 4, 5) forall (_ >= 4)) //false
println(List(11, 7, 13, 4, 5) forall (_ >= 4)) //true
println
//exists 当存在(至少有一个满足)元素满足条件时 返回true 否则返回false
//def exists(p: A => Boolean): Boolean
println(List(1, 7, 3, 4, 5) exists (_ >= 4)) //true
println(List(11, 7, 13, 4, 5) exists (_ >= 4)) //true
println
def hastotallyZeroRow(m: List[List[Int]]) = m exists (row => row forall (_ == 0))
val m = List(List(1, 0, 0), List(0, 1, 0), List(0, 0, 0))
println(hastotallyZeroRow(m)) //true