一、简单示例
1、map示例
首先来一个简单的小例子,集合中一些数据,看这些数据是如何经过map
的。
代码如下(示例):
def main(args: Array[String]): Unit = {
val sparkconf = new SparkConf().setMaster("local[*]").setAppName("test")
var sc = new SparkContext(sparkconf);
val rdd = sc.makeRDD(List(1,2,3,4),1)
//num只出现了一次,而且是按照顺序出现的,则num可以通过 _ 来表示
val value = rdd.map(num=>{
println(">>>>"+num);
num
})
val value2 = value.map(num=>{
println("-----"+num);
num
})
value2.collect()
sc.stop();
}
输出:
>>>>1
-----1
>>>>2
-----2
>>>>3
-----3
>>>>4
-----4
如上所示,在一个分区到时候是将分区内的所有的数据,依次执行完所有的操作,再执行下一条数据。
当设置为两个分区的时候。
val rdd = sc.makeRDD(List(1,2,3,4),2)
有如下输出,证明两个分区的数据并行执行,每个分区都是一条一条执行。分区内的数据也是按照顺序执行的。
>>>>1
>>>>3
-----3
-----1
>>>>4
>>>>2
-----4
-----2
2、mapPartitions示例
def main(args: Array[String]): Unit = {
val sparkconf = new SparkConf().setMaster("local[*]").setAppName("test")
var sc = new SparkContext(sparkconf);
val rdd = sc.makeRDD(List(1,2,3,4),2)
val value = rdd.mapPartitions(iter=>{
println(">>>>");
iter
})
value.collect()
sc.stop();
}
mapPartitions和map的入参有一点区别,map
是一个num
数值,但是mapPartitions
是一个迭代器,这个迭代器代表一个分区的全部数据,例如在2个分区中,只打印了两次。
>>>>
>>>>
如此说明,mapPartitions是一个分区一个分区的执行,当分区内所有数据处理完成,才会释放分区的数据。如此看来这两个功能及其类似。另外这两个分区的执行是无序的,如果只想取某一个分区的数据可使用mapPartitionsWithIndex(index,iter)
来指定分区。
但是,如果想得到分区中的最大值呢?这个功能mapPartitions
实现起来就有先天的优势了。
def main(args: Array[String]): Unit = {
val sparkconf = new SparkConf().setMaster("local[*]").setAppName("test")
var sc = new SparkContext(sparkconf);
val rdd = sc.makeRDD(List(1,2,3,4),2)
val value = rdd.mapPartitions(iter=>{
List(iter.max).iterator
})
value.collect().foreach(println)
sc.stop();
}
总结
- 数据处理角度
Map
算子是分区内一个数据一个数据的执行,类似于串行操作。而mapPartitions
算子是以分区为单位进行批处理操作。 - 功能的角度
Map
算子主要目的将数据源中的数据进行转换和改变。但是不会减少或增多数据。
MapPartitions 算子需要传递一个迭代器,返回一个迭代器,没有要求的元素的个数保持不变,所以可以增加或减少数据 - 性能的角度
Map
算子因为类似于串行操作,所以性能比较低,而是mapPartitions
算子类似于批处
理,所以性能较高。但是mapPartitions
算子会长时间占用内存,那么这样会导致内存可能不够用,出现内存溢出的错误。所以在内存有限的情况下,不推荐使用。可以使用map
操作。