spark是分布式计算框架,RDD是spark对分布式内存数据的抽象,可以认为RDD是spark分布式算法的数据结构;spark会将算法(RDD上的一连串操作)翻译为DAG形式的工作流进行调度,并进行task的分发;
RDD可以认为是分布式的数组,数组中的每条记录可以是用户自定义的任意数据结构;RDD是spark的基本计算单元,也是核心的数据结构,spark根据RDD的依赖关系来决定调度顺序,通过对RDD的操作形成了整个spark应用程序;
textAsFile()
)parallelize
或makeRDD
将单机数据创建为分布式RDD转换算子,延迟计算,一个RDD施加转换算子后,并不会立即执行,只有当遇到action算子后才会将之前的所有计算,统一计算;
转换算子用来完成作业中间过程的处理,并不会触发sparkContext提交job作业;
执行算子,立即计算,会触发sparkContext提交job作业,并触发计算;
一对一
RDD内的每个数据项----f(用户自定义函数)—>新RDD的每个数据项
map()是将函数用于RDD中的每个元素,将返回值构成新的RDD。
flatmap()是将函数应用于RDD中的每个元素,将返回的迭代器的所有内容构成新的RDD
map是对每一行进行处理,flatmap对每一行进行处理后 如果处理结果的长度是1,那么就和map操作一样;如果处理后是个List或者Array类型,那么就会将这个List或者Array的每个元素变成1行
scala> val rdd = sc.parallelize(List("coffee panda","happy panda","happiest panda party"))
rdd: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[0] at parallelize at <console>:24
scala> rdd.map(x=>x).collect
res0: Array[String] = Array(coffee panda, happy panda, happiest panda party)
scala> rdd.flatMap(x=>x.split(" ")).collect
res1: Array[String] = Array(coffee, panda, happy, panda, happiest, panda, party)
scala> val rdd1 = sc.parallelize(List(1,2,3,3))
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[3] at parallelize at <console>:24
scala> rdd1.map(x=>x+1).collect
res2: Array[Int] = Array(2, 3, 4, 4)
scala> rdd1.flatMap(x=>x.to(3)).collect
res3: Array[Int] = Array(1, 2, 3, 2, 3, 3, 3)
从上面就可以总结出来这个规律,flaMap就是先map,然后再对每个元素flat
在实际处理中,多处理的是RDD的键值对,所以下面我举这样一个例子:
//定义一个键值对RDD,长度只有1行,已经够说明具体情况
val tmp = List(
("zhangjie",Array(("多余","11.0,25.0",1),
("2511&&&&第二,长度11","第二俄日的长度11",0),
("2522&&&&第二,长度22","第二俄日的长度22",0),
("2533&&&&第二,长度33","第二俄日的长度33",0)))
)
val tmp1 = sc.parallelize(tmp).repartition(1)
// List(imei_expo(0).split(",")(0),imei_expo(0).split(",")(1))是一个List,下面分别对比下map和flatmap的区别
val tmp2 = tmp1.flatMap{t=>
val imei_expo = t._2.filter(t=> t._3==1).map(_._2) //曝光-下载,根据逗号组成的字符串
List(imei_expo(0).split(",")(0),imei_expo(0).split(",")(1))
}
//scala> tmp2.take(10).foreach(println)
//11.0
//25.0
val tmp2 = tmp1.map{t=>
val imei_expo = t._2.filter(t=> t._3==1).map(_._2) //曝光-下载,根据逗号组成的字符串
List(imei_expo(0).split(",")(0),imei_expo(0).split(",")(1))
}
// scala> tmp2.count()
// res63: Long = 1
// days_keywordcuts.map(days_keywordcuts => (days_keywordcuts, imei_expo(0)))的长度是3,所以最后的tmp2的长度也是3
val tmp2 = tmp1.flatMap{t=>
val imei_expo = t._2.filter(t=> t._3==1).map(_._2) //曝光-下载,根据逗号组成的字符串,长度为1
val days_keywordcuts = t._2.filter(t=> t._3==0).map(_._1) //长度为多个
days_keywordcuts.map(days_keywordcuts => (days_keywordcuts, imei_expo(0)))
}
//scala> tmp2.take(10).foreach(println)
//(2511&&&&第二,长度11,11.0,25.0)
//(2522&&&&第二,长度22,11.0,25.0)
//(2533&&&&第二,长度33,11.0,25.0)