高级算子详解
mapPartitions
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData: Array[Int] = Array(1, 2, 3)
val sc = new SparkContext(conf)
//分成2个partition处理
val initDataRDD: RDD[Int] = sc.parallelize(targetData,2)
//它的作用就是用一个分区的一块用map处理,iter就是来的这个分区的数据
//如果数据量小可以使用它,如果数据量大,那么很容易出现OOM
//它的返回值也是一个Iterator
val res: RDD[Int] = initDataRDD.mapPartitions(iter => {
iter.take(1)
})
res.collect().foreach(println)
sc.stop()
}
}
mapPartitionsWithIndex
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData: Array[Int] = Array(1, 2, 3)
val sc = new SparkContext(conf)
val initDataRDD: RDD[Int] = sc.parallelize(targetData,2)
//这个和mapPartitions就是有一个分区的编号,第一个参数是分区号
//第二个参数是每一个分区的数据
//返回的值也是一个Iterator
//下面得到每一个分区的第一个值
val res: RDD[(Int, Int)] = initDataRDD.mapPartitionsWithIndex((index, iter) => {
Array((index, iter.take(1).next())).iterator
})
res.collect().foreach(item=>{
println(item)
})
sc.stop()
}
}
结果
(0,1)
(1,2)
sample
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData: Array[Int] = Array(1, 2, 3)
val sc = new SparkContext(conf)
val initDataRDD: RDD[Int] = sc.parallelize(targetData,2)
//根据随机种子抽取随机的值
//第一个参数设置是否抽取以后放回去,true表示放回抽取的集合
//第二个参数是设置抽取的比例
//第三个参数是设置随机种子,如果不设置那么就是随机数
initDataRDD.sample(false,0.5,9).collect().foreach(println)
sc.stop()
}
}
结果
2
3
groupByKey
图解
- 在有shuffle的操作中,它们中间会产生隐私的ShuffledRDD,它的作用是读取上游的key,把相同的key放在一起,然后通过groupByKey里面的一个map操作,把key相同的数据放在一个集合里面
reduceByKey
图解
cogroup
图解
join
图解
sortByKey
图解
union(并集)
原理
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData1: Array[Int] = Array(1, 2, 3,5)
val targetData2: Array[Int] = Array(1, 2, 3,8)
val sc = new SparkContext(conf)
val initDataRDD1: RDD[Int] = sc.parallelize(targetData1,2)
val initDataRDD2: RDD[Int] = sc.parallelize(targetData2,2)
initDataRDD1.union(initDataRDD2).collect().foreach(println)
sc.stop()
}
}
结果
1
2
3
5
1
2
3
8
结果验证
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData1: Array[Int] = Array(1, 2, 3,5)
val targetData2: Array[Int] = Array(1, 2, 3,8)
val sc = new SparkContext(conf)
val initDataRDD1: RDD[Int] = sc.parallelize(targetData1,2)
val initDataRDD2: RDD[Int] = sc.parallelize(targetData2,2)
initDataRDD1.union(initDataRDD2)
.mapPartitionsWithIndex((index,ite)=>{
println("==========")
println("我是分区"+index)
ite
}).foreach(println)
sc.stop()
}
}
输出结果(可以看到union把之前的分区的数据都直接拉过来了,就是原来2个分区和2个分区合并得到的分区就是4个)
==========
我是分区0
1
2
==========
我是分区1
3
5
==========
我是分区2
1
2
==========
我是分区3
3
8
intersection(交集)
原理
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData1: Array[Int] = Array(1, 2, 3,5)
val targetData2: Array[Int] = Array(1, 2, 3,8)
val sc = new SparkContext(conf)
val initDataRDD1: RDD[Int] = sc.parallelize(targetData1,2)
val initDataRDD2: RDD[Int] = sc.parallelize(targetData2,1)
initDataRDD1.intersection(initDataRDD2)
.mapPartitionsWithIndex((index,ite)=>{
println("==========")
println("我是分区"+index)
ite
}).foreach(println)
sc.stop()
}
}
结果
==========
我是分区0
3
==========
我是分区1
1
==========
我是分区2
2
distinct
原理
测试
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData1: Array[Int] = Array(1, 2, 3,5)
val targetData2: Array[Int] = Array(1, 2, 3,8)
val sc = new SparkContext(conf)
val initDataRDD1: RDD[Int] = sc.parallelize(targetData1,2)
val initDataRDD2: RDD[Int] = sc.parallelize(targetData2,2)
initDataRDD1.union(initDataRDD2).distinct()
.mapPartitionsWithIndex((index,ite)=>{
println("==========")
println("我是分区"+index)
ite
}).foreach(println)
sc.stop()
}
}
结果
==========
我是分区0
8
==========
我是分区1
1
5
==========
我是分区2
2
==========
我是分区3
3
aggregate
原理
测试
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val targetData1: Array[Int] = Array(1, 2, 3,5)
val sc = new SparkContext(conf)
val initDataRDD1: RDD[Int] = sc.parallelize(targetData1,2)
//第一个是初始值
//第二个是分区内的计算
//第三个是分区之间的计算
val res: Int = initDataRDD1.aggregate(0)(_ + _, _ + _)
println(res)
sc.stop()
}
}
结果
11
cartesian(迪卡尔集)
测试
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val colors: Array[String] = Array("金色", "姚黑", "天空蓝")
val phone: Array[String] = Array("小米", "华为")
val sc = new SparkContext(conf)
val colorRDD: RDD[String] = sc.parallelize(colors,2)
val phoneRDD: RDD[String] = sc.parallelize(phone,2)
colorRDD.cartesian(phoneRDD)
.mapPartitionsWithIndex((index,iter)=>{
println("===============")
println("我是"+index)
iter
}).foreach(println)
sc.stop()
}
}
结果
===============
我是0
(金色,小米)
===============
我是1
(金色,华为)
===============
我是2
(姚黑,小米)
(天空蓝,小米)
===============
我是3
(姚黑,华为)
(天空蓝,华为)
图解
coalesce(合并分区)
测试在没有使用前
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val colors: Array[String] = Array("金色", "姚黑", "天空蓝")
val phone: Array[String] = Array("小米", "华为")
val sc = new SparkContext(conf)
val colorRDD: RDD[String] = sc.parallelize(colors,2)
val phoneRDD: RDD[String] = sc.parallelize(phone,2)
colorRDD.cartesian(phoneRDD)
.mapPartitionsWithIndex((index,iter)=>{
println("===============")
println("我是"+index)
iter
}).foreach(println)
sc.stop()
}
}
结果是有4个分区
===============
我是0
(金色,小米)
===============
我是1
(金色,华为)
===============
我是2
(姚黑,小米)
(天空蓝,小米)
===============
我是3
(姚黑,华为)
(天空蓝,华为)
使用以后
object ScalaBibao {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("ScalaBibao")
.setMaster("local")
val colors: Array[String] = Array("金色", "姚黑", "天空蓝")
val phone: Array[String] = Array("小米", "华为")
val sc = new SparkContext(conf)
val colorRDD: RDD[String] = sc.parallelize(colors,2)
val phoneRDD: RDD[String] = sc.parallelize(phone,2)
//coalesce第二个参数表示是否使用shuffle,使用shuffle会使得数据更加的均匀
colorRDD.cartesian(phoneRDD).coalesce(2, false)
.mapPartitionsWithIndex((index,iter)=>{
println("===============")
println("我是"+index)
iter
}).foreach(println)
sc.stop()
}
}
结果
===============
我是0
(金色,小米)
(金色,华为)
===============
我是1
(姚黑,小米)
(天空蓝,小米)
(姚黑,华为)
(天空蓝,华为)
图解
repartition
(就是coalesce使用shuffle)
coalesce(numPartitions, shuffle = true)
图解
- 它进行shuffle的原理就是在原来的数据前面加上了一个前缀,用来方便shuffle
takeSample
colorRDD.takeSample(true,1)
后面的数字表示指定抽取几个数据
Shuffle调优
对于上面的图对比调优的参数
spark.reducer.maxSizeInFlight | reduceTask拉取数据的缓存区的大小,如果内存足够,那么适当的调大 有助于减少网络io的次数,提高吞吐量,默认是48M |
spark.shuffle.compress | 是否开启maptask的写出数据的压缩,默认是true |
spark.io.compression.codec | spark的压缩格式,默认是lz4,spark提供了lz4 , lzf , snappy , zstd |
spark.shuffle.file.buffer | maptask写缓冲区的大小,默认是32k,适当的调大可以减少磁盘的io次数 |
spark.shuffle.io.maxRetries | reduceTask如果拉取失败的时候最大的尝试次数,默认是3次,就是如果maptask频繁的出现gc的时候,可能会造成任务的执行失败,可以通过这个参数进行适当的调节,适当的调大可以提高reduceTask的成功率 |
spark.shuffle.io.retryWait | reduceTask拉取数据失败有以后,重试的时间间隔,适当的调大可以提高reduceTask的成功率,默认是5秒 |
spark.shuffle.io.numConnectionsPerpeer | 集群可以重用的网络连接默认是1 |
spark.shuffle.io.preferDirectBufs | 是否开启堆外内存默认是true |
spark.shuffle.sort.bypassMergeThreshold | 默认值是200,如果在200之内,并且没有预聚合的功能,那么就不用在mapTask写的阶段对数据进行排序,提高性能 |