spark复习笔记(4):RDD变换
一、RDD变换
1.返回执行新的rdd的指针,在rdd之间创建依赖关系。每个rdd都有一个计算函数和指向父rdd的指针
Spark是惰性的,因此除非调用某个转换或动作,否则不会执行任何操作,否则将触发工作创建和执行。
2.map()是对每个元素进行变换,应用变换函数,返回的是一个新的分布式数据集,map就是对分区中的每个元素进行一个函数的调用,所以导致出现了那么多;
map() //对每个元素进行变换,应用变换函数,(T)=>V,
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object WordCountDemoScala1 { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setAppName("WordCountDemoScala1"); conf.setMaster("local"); val sc = new SparkContext(conf); val rdd1=sc.textFile("E:/studyFile/data/test.txt"); val rdd2 = rdd1.flatMap(line=>{ println("flatmap:"+line);line.split(" ")}) val rdd3 = rdd2.map((_,1)); val rdd4 = rdd3.reduceByKey(_ + _); val r = rdd4.collect(); r.foreach(println) } }
3.mapPartitions() //对RDD上的每个分区应用一个函数,返回一个新的RDD,输入的是一个迭代器,返回的是一个新的迭代器,可以对每个分区进行函数处理,而不是每个函数。现在是2个线程,四条数据,两条一切割
package com.jd.test
import org.apache.spark.{SparkConf, SparkContext}
object WordCountDemo3 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf();
conf.setAppName("WordCountDemo3");
conf.setMaster("local[2]");//local[2]表示开启2个线程来模拟并发程序
val sc = new SparkContext(conf );
val rdd1 = sc.textFile("E:/studyFile/data/test.txt",2);//设置最小分区数为2
val rdd2 = rdd1.flatMap(_.split(" "));
val rdd3 = rdd2.mapPartitions(it=>{
import scala.collection.mutable.ArrayBuffer;
val buf = ArrayBuffer[String]();
val tname = Thread.currentThread().getName
println(tname+":"+"mapPartitions start");//将线程加入该分区
for (e<-it){
buf.+=("_"+e);
}
buf.iterator
});
val rdd4 = rdd3.map(word=>{
val tname=Thread.currentThread().getName
println(tname+":map"+word);
(word,1)})
val rdd5= rdd4.reduceByKey(_ + _);
rdd5.foreach(println)
}
}
下面演示并发线程数为4,最小分区数为4
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object WordCountDemo3 { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setAppName("WordCountDemo3"); conf.setMaster("local[4]");//local[4]表示并发线程数为4 val sc = new SparkContext(conf ); val rdd1 = sc.textFile("E:/studyFile/data/test.txt",4);//设置最小分区数为4 val rdd2 = rdd1.flatMap(_.split(" ")); val rdd3 = rdd2.mapPartitions(it=>{ import scala.collection.mutable.ArrayBuffer; val buf = ArrayBuffer[String](); val tname = Thread.currentThread().getName println(tname+":"+"mapPartitions start");//将线程加入该分区 for (e<-it){ buf.+=("_"+e); } buf.iterator }); val rdd4 = rdd3.map(word=>{ val tname=Thread.currentThread().getName println(tname+":map"+word); (word,1)}) val rdd5= rdd4.reduceByKey(_ + _); rdd5.foreach(println) } }
4.filter() //过滤器,(T)=>Boolean,返回一个唯一满足滤条件元素的RDD
5.flatMap() //压扁,T=>TraversableOnce[U]同上,(Int,Interator)=>Iterator<U>
6.sample(withReplacement,fraction,seed) //采样,返回采样的RDD子集。withReplacement元素是否可以多次采样,fraction:期望采样的数量[0,1]
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object SampleDemo { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setAppName("SampleDemo"); conf.setMaster("local"); val sc = new SparkContext(conf); val rdd1 = sc.textFile("E:/studyFile/data/test.txt"); val rdd2= rdd1.flatMap(_.split(" ")); val rdd3= rdd2.sample(false,0.5);//可以替换,被抽中的概率是0.5 rdd3.collect().foreach(println) } }
7.mapPartitionsWithIndex(func) //同上(int Iterator<T>)=Iterator<u>。与map函数相类似,但是也提供一个代表分区索引的整数的函数,所以当运行一个T类型的RDD的时候这个函数的类型必须是(Int,Iterator<T>)类型的,
8.union() //类似于mysql的union操作,返回一个新的数据集,其中包含元数据集和参数中 数据集的并集。
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object UnionDemo1 { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setMaster("local"); conf.setAppName("UnionDemo1") val sc = new SparkContext(conf); val rdd1 = sc.textFile("E:/studyFile/data/test.txt"); //所有hello行 val helloRDD=rdd1.filter(_.toLowerCase.contains("hello")); //所有world行 val worldRDD=rdd1.filter(_.toLowerCase.contains("world")); val allRDD=helloRDD.union(worldRDD); allRDD.collect().foreach(println) } }
//select * from persons where id<10 unoin select * from id persons where id>29
9.intersection //取出交集,提取两个rdd都含有的元素
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object IntersectionDemo1 { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setAppName("IntersectionDemo1"); conf.setMaster("local"); val sc = new SparkContext(conf); val rdd1 = sc.textFile("E:/studyFile/data/test.txt"); val rdd2 = rdd1.flatMap(_.split(" ")); val helloRDD = rdd2.filter(_.toLowerCase.contains("llo")); val worldRDD=rdd2.filter(_.toLowerCase.contains("wor")); val intersectRDD= helloRDD.intersection(worldRDD); intersectRDD.collect().foreach(println) } }
10.distinct([numTasks]) //去重操作
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object DistinctDemo{ def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setMaster("local") conf.setAppName("DistinctDemo"); val sc = new SparkContext(conf); val rdd1 =sc.textFile("E:/studyFile/data/test.txt"); val rdd2 = rdd1.flatMap(_.split(" ")); val rdd3= rdd2.distinct() rdd3.collect().foreach(println) } }
11.reduceByKey(*) //按照key进行聚合
12.groupByKey() //按照key进行分组(K,V)=>(k,Iterable<V>),当你在(K,V)对上调用这个函数的时候,结果会返回一个可以迭代的量(K,Iterable<V>),相同key值的数据别合在一起了。
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object GroupByKeyDemo { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setAppName("GroupByKeyDemo") conf.setMaster("local"); val sc = new SparkContext(conf); val rdd1 = sc.textFile("E:/codes/stus.txt"); val rdd2 = rdd1.map(line=>{ val key = line.split(" ")(3) (key,line); }); //通过key来进行分组 val rdd3 = rdd2.groupByKey(); rdd3.collect().foreach(t=>{ val key = t._1; println(key+":=================================================================>") for(e <- t._2){ println(e) } }) } }
13.reduceByKey(func,[numTasks])
当在(k,v)键值对上调用一个数据结集的时候,结果返回一个键值对,其中每个键对应的值都给定一个聚合函数func进行聚合,其类型必须是(V,V)=>V。同groupByKey一样,reduce的任务数通过第二个可选的参数进行指定
14.sortByKey([ascending],[numTasks]) //按key进行排序
15.join(otherDataset,[numTasks]) //横向连接,按key来进行联合,当你调用数据集(K,V)和(K,W)的时候,会返回一个新的数据集(K,(V,W))
16.cogroup协分组 //(K,V).cogroup(K,W)=>(K,(Iterable<V>,Iterator<W>))
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object CogroupDemp { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setAppName("CogroupDemp"); conf.setMaster("local[4]"); val sc = new SparkContext(conf); val rdd1 =sc.textFile("E:/codes/cogroup-1.txt") val rdd2 =rdd1.map(line=>{ val arr = line.split(" "); (arr(0),arr(1)); }); val rdd3 = sc.textFile("E:/codes/cogroup-2.txt"); val rdd4 = rdd3.map(line=>{ val arr = line.split(" "); (arr(0),arr(1)) }); val rdd = rdd2.cogroup(rdd4); rdd.collect().foreach(t=>{ println(t._1+":============================================>") for(e<-t._2._1){ println(e) } for (e<-t._2._2){ println(e) } }) } }
17.cartesian(otherDataset) //笛卡尔积,RR[T]=>RDD[(T,U)]。调用类型T和类型U的数据集,返回(T,U)所对的数据集
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} //计算笛卡尔积 object cartesianDemo { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setMaster("local[4]"); conf.setAppName("cartesianDemo"); val sc = new SparkContext(conf); val rdd1 = sc.parallelize(Array("tom","jack","peter","stone","mike")); val rdd2 = sc.parallelize(Array("1234","2345","3456","5678")); val rdd3 = rdd1.cartesian(rdd2); rdd3.collect().foreach(t=>println(t)) } }
18.pipe //将rdd的元素传递给脚本或者命令
19.coalesce(numPartitions) //降低RDD中的分区数到指定的分区数
20.repartition //重新分区,分区数可增可减
package com.jd.test import org.apache.spark.{SparkConf, SparkContext} object SaveAsTextDemo { def main(args: Array[String]): Unit = { val conf = new SparkConf(); conf.setMaster("local"); conf.setAppName("SaveAsTextDemo"); val sc = new SparkContext(conf); val rdd1 = sc.textFile("E:/codes/test.txt"); val rdd2 = rdd1.flatMap(_.split(" ")); val rdd3 = rdd2.map((_,1)); //Merge the values for each key using an associative and commutative reduce function. This will // * also perform the merging locally on each mapper before sending results to a reducer, similarly // * to a "combiner" in MapReduce. val rdd4 = rdd3.reduceByKey(_ + _,3);//指定分区数为3,按key聚合,同时还指定分区数 rdd4.saveAsTextFile("E:/codes/out") } }
21.repartiotion //在RDD中重新进行shuffle操作,来随机的创建更多或者更少的分区,以及通过他们来进行分区的平衡,这经常用在网络间数据的shuffle
22.repartitionAndSortWithinPartitions(partitioner) //再分区并且排序
二、RDD Action
------------------------------------------------------------------
1.collect() //在客户端返回数据集的所有元素来作为数组,这在筛选器或其他操作返回足够小的数据子集之后非常有用。
2.reduce() //使用函数func(携带两个参数并返回一个参数)聚合数据集的元素,函数应该可以通信以及关联
3.count //返回数据集中RDD元素的个数,形成数组,手机RDD元素形成数组
4.first //只取第一个元素
5.take(n) //去除前n个元素
6.takeOrdered(n,[ordering]) //返回RDD的前n个元素,使用自然顺序或者用户比较器
7.saveAsTextFile(path) //保存到文件中去
8.saveAsSequenceFile(path) //保存成序列文件
9.countByKey() //按照key进行统计
三、Spark数据倾斜
数据倾斜问题,map阶段对key进行重新划分