什么是RDD:
RDD叫做分布式数据集,是Spark最基本的数据抽象,代码中是一个抽象类,它代表的是一个不可变、可分区、里面的元素可并行计算的集合。
RDD里面封装的是计算逻辑。
RDD的属性:1. 一个分区(Partition),即数据集的基本组成单位。
2. 一个计算每个分区的函数。
3. RDD之间的依赖关系。
4. 一个Patitioner(即RDD的分片函数)
5. 一个列表,存取每个Partition的优先位置(preferred Location)
Rdd的特点:
Rdd表示只读的分区的数据集,对RDD的改动,只能进行RDD的转换操作,由一个RDD得到一个新的RDD,新的RDD包含了从其他RDD衍生所必需的信息,RDD之间存在依赖,RDD执行时按照血缘关系延时计算的,如果血缘关系较长,可以通过RDD的持久化切断血缘关系。
分区:
RDD逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候会通过一个compute函数得到每个分区的数据,如果RDD是通过已有的文件系统构建,那么它的compute函数则只能读取指定文件系统中的数据,如果RDD是通过其他的RDD转换过来的,那么它的compute函数则是执行转换逻辑将其他RDD的数据进行转换。
这里生成的文件是八个,原因是因为它默认的cp是2个,但是它源码里面取得是num,Local*取得是你最大的内核数,我是8个内核,8和2比较,取最大值,生成了8个分区,所以8个内核并行计算,生成的文件是8个。
这里生成的2个文件,是因为它的源码里面取得是最小的分区数,它的min是2,所以生成的是2个文件。
如果你代码自定义分区数,一切以你代码为最高执行标准,所以你给多少它生成多少,特定情况除外,如min,你自定义2时,也许他会生成3个文件,
例子如:你文件是12345
你自定义为2个分区,可是它会生成三个,因为它采用是hadoopFile,有自己的切分规则,它除不尽,所以是三个。
算子:
Map算子:
Map算子是对每一条数据做操作,作用:是返回一个新的RDD,该RDD由每一个输入元素经过func函数转换后组成。
例子:
package m.s.k
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark_MapOper {
def main(args: Array[String]): Unit = {
//Local模式
//创建SparkConf对象,并部署计算环境
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("MapOper")
//创建SparkContext上下文对象
val sc: SparkContext = new SparkContext(conf)
//map算子
val listRdd: RDD[Int] = sc.makeRDD(1 to 10)
val mapRDD: RDD[Int] = listRdd.map(_*2)
//打印
mapRDD.collect().foreach(println)
}
}
MapPartitions(func)案例:
作用:类似于map,独立的在RDD的每一个分片上运行,因此在类型为T的RDD上运行时,func的函数类型必须是Iterator[T]=>Itertor[U],假如有N个元素,有M个分区,那么map的函数将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区。
package m.s.k
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark_MapPartitiomsOper {
def main(args: Array[String]): Unit = {
//local模式
//创建Sparkconf对象,并部署计算机运行环境
val conf = new SparkConf().setMaster("local[*]").setAppName("MapPartitions")
//创建SparkContext上下文对象
val sc: SparkContext = new SparkContext(conf)
//MapPartitions
val listRDD: RDD[Int] = sc.makeRDD(1 to 10)
//mappartition可以对RDD里面所有的分区进行遍历
//MapPartition的效率优于map算子,减少了发送到执行器执行的交互次数,但它可能存在内存溢出
val mapPartition: RDD[Int] = listRDD.mapPartitions({
_.map(_* 2)
})
//打印
mapPartition.collect().foreach(println)
}
}
MapPartitionWithIndex(func)案例:
作用:类似于MapPartitions,但是func带有一个整数参数表示分片的索引值、因此,在类型为T的RDD上运行时、func的函数类型必须是Itertor[T]=>Itertor[U]
需求:创建一个RDD,使每个元素跟所在分区形成一个元组组成一个新的RDD
package m.s.k
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark_MapPartitionsWithIndex {
def main(args: Array[String]): Unit = {
//Local模式
//创建SparkConf,并部署计算环境
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("MapWithIndex")
//创建SparkContext上下文对象
val sc: SparkContext = new SparkContext(conf)
//MapPartitionsWithIndex
val listRDD: RDD[Int] = sc.makeRDD(1 to 10,2)
val tupleRDD: RDD[(Int, String)] = listRDD.mapPartitionsWithIndex {
case (num, datas) => {
datas.map((_, "分区号:" + num))
}
}
//打印
tupleRDD.collect().foreach(println)
}
}
所有算子里面的计算功能都是Executor来做的,如_*2计算,那怎么选择交给哪一个Executor呢,这个是由Driver决定的,它会判断哪一个Executoe计算会快一点。
FlatMap(func)案例:
作用:类似于map,但是每一个输入元素可以被映射为0或多个输出元素(所以func应该一个序列,而不是单一的元素)
需求:创建一个元素为1-5的RDD,运用flatMap创建一个新的RDD,新的RDD为原RDD的每个元素的2倍(2,4,6,8,10)
package m.s.k
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark_FlatMap {
def main(args: Array[String]): Unit = {
//Local模式
//创建SparkConf对象、并部署运行模式
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("FlatMapOper")
//创建SparkContext上下文对象
val sc: SparkContext = new SparkContext(conf)
//Flatmap算子
val listRDD: RDD[List[Int]] = sc.makeRDD(Array(List(1,2),List(3,4)))
val flatMapRDD: RDD[Int] = listRDD.flatMap(datas=>datas)
//打印
flatMapRDD.collect().foreach(println)
}
}
Map和MapPartition的区别:
1. map:每次处理一条数据
2. mappartition:每次处理一个分区的数据,当现在这个分区的数据处理完成之后,原分区的数据才能得到释放,可以导致OOM。
3. 开发介意:当你的内存空间足够大时,建议使用MapPartition,以提高效率。
glom的案例:
作用:将每一个分区形成一个数组(就是把每个分区的数据放到一个数组里面),形成新的RDD类型时:RDD[Array[T]]
需求:创建一个4个分区的RDD,并将每个分区的数据放到一个数组
package m.s.k
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark_glomOper {
def main(args: Array[String]): Unit = {
//Local模式
//创建SparkConf,并部署计算运行环境
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("glomOper")
//创建SparkContext上下文对象
val sc = new SparkContext(conf)
//glom算子
val listRDD: RDD[Int] = sc.makeRDD(1 to 16,4)
val glomRDD: RDD[Array[Int]] = listRDD.glom()
//打印
glomRDD.collect().foreach(array=>{
println(array.mkString(","))
})
}
}
Group(func)案例:
作用:分组,按照传入函数的返回值进行分组,将相同的key对应的值放到一个迭代器。
需求:创建一个RDD,按照元素模以2的值进行分组。
package m.s.k
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark_GroupByOper {
def main(args: Array[String]): Unit = {
//Local模式
//创建SparkConf,部署计算
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("GroupByOper")
//创建上下文对象
val sc: SparkContext = new SparkContext(conf)
//GroupBy算子
val listRDD: RDD[Int] = sc.makeRDD(1 to 6)
//1:1,3,5
//0:2,4,6
//生成数据,按照指定的规则进行分组,分组后的数据形成了对偶元组(K-V):K表示分组的key,V表示分组的集合
val GroupByRDD: RDD[(Int, Iterable[Int])] = listRDD.groupBy(_%2)
//打印
GroupByRDD.collect().foreach(println)
}
}
Filet(func)案例:
作用:过滤,返回一个新的RDD,该RDD由经过func函数计算后返回值为true的输入元素组成。
package m.s.k
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Spark_FilterOper {
def main(args: Array[String]): Unit = {
//创建SparkConf对象,并部署计算环境
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("FilterOper")
//创建SparkContext上下文对象
val sc = new SparkContext(conf)
//Filter算子过滤
val listRDD: RDD[Int] = sc.makeRDD(1 to 10)
val filterRDD: RDD[Int] = listRDD.filter(_%2==0)
//打印
filterRDD.collect().foreach(println)
}
}