Spark3.x入门到精通-阶段六(RDD高级算子详解&图解&shuffle调优)

高级算子详解

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.codecspark的压缩格式,默认是lz4,spark提供了lz4lzfsnappy, zstd
spark.shuffle.file.buffermaptask写缓冲区的大小,默认是32k,适当的调大可以减少磁盘的io次数
spark.shuffle.io.maxRetriesreduceTask如果拉取失败的时候最大的尝试次数,默认是3次,就是如果maptask频繁的出现gc的时候,可能会造成任务的执行失败,可以通过这个参数进行适当的调节,适当的调大可以提高reduceTask的成功率
spark.shuffle.io.retryWaitreduceTask拉取数据失败有以后,重试的时间间隔,适当的调大可以提高reduceTask的成功率,默认是5秒
spark.shuffle.io.numConnectionsPerpeer集群可以重用的网络连接默认是1
spark.shuffle.io.preferDirectBufs是否开启堆外内存默认是true
spark.shuffle.sort.bypassMergeThreshold默认值是200,如果在200之内,并且没有预聚合的功能,那么就不用在mapTask写的阶段对数据进行排序,提高性能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

工作变成艺术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值