Spark中的RDD数据模型

本文深入介绍了Spark中的Resilient Distributed Datasets (RDD) 数据模型,包括RDD的并行度与分区原理,从内存和文件创建RDD的方式,以及RDD的各种转换算子如map、mapPartitions、filter、reduceByKey等的详细用法,并提供了实际案例,如从日志文件中统计省份广告点击数量。
摘要由CSDN通过智能技术生成

目录

1.RDD并行度与分区

1.1从内存中创建RDD -- 分区

1.2从文件中创建RDD -- 分区

2.RDD转换算子

2.1转换算子

2.1.1map算子

2.1.2 mapPartitions算子

2.1.3mapPartitionsWithIndex算子

2.1.4 flatMap算子 

2.1.6 groupBy算子

 2.1.7 filter()算子

2.1.8 coalesce 算子

 2.1.9 distinct算子

 2.1.10 sortBy算子

2.1.11 intersection 、union、subtract、zip算子

2.1.12 partitionBy 算子 

2.1.13 reduceByKey、groupByKey 算子 

2.1.14 aggregateByKey、foldByKey 算子

 2.1.15 combineByKey 算子

2.1.16 join、leftOuterJoin、coGroup算子

 案例实操:从log文件中统计出每一个省份每个广告被点击数量排行的Top3


1.RDD并行度与分区

Spark可以将一个作业切分多个任务后,发送给Executor节点并行计算,而能够并行计算的任务数量我们称之为并行度。

1.1从内存中创建RDD -- 分区

分区的源码如下:

(0 until numSlices).iterator.map{
    i => val start = ((i * length)/numSlices).toInt
    val end = ((i+1)*length/numSlices).toInt
    (start,end)
}

numSlices表示分区数,i表示第i个分区,start和end表示该分区的数据从start开始取,取到end-1

例如:

val rdd1 : RDD[Int] = context.makeRDD(
      Seq(1, 2, 3, 4,5),2
    )

 表示从内存创建RDD,分区数为2,按照公式计算每个分区的数据具体如下:

i -->(0,1)

分区0:start = (0*5)/2 = 0   end = (0+1)*5/2 = 2   所以分区0的数据为(1,2)

分区1:start = (1*5)/2 = 2   end = (1+1)*5/2 = 5   所以分区1的数据为(3,4,5) 

 如果创建RDD时没有指明分区数,则按照默认值分区

源码中的默认值:

scheduler.conf.getInt("spark.default.parallelism",totalCores)
totalCores : 当前环境的总(虚拟)核数  
分区设置的优先级:方法参数>配置参数>环境配置(默认)

1.2从文件中创建RDD -- 分区

1.spark读取文件底层其实是hadoop读取文件 
 2.spark的分区数其实来自于hadoop读取文件时的切片
val rdd: RDD[String] = context.textFile("data/word.txt")
textFile可以在读取文件时设定分区,如果不设定存在默认值math.min(defaultParallelism,2)

 defaultParallelism为默认并行度,取决于当前环境的虚拟核数

切片规则:

文件大小totalSize,预计每个分区的字节大小goalSize = totalSize/numSplits
例如文件大小5字节,分为3个分区
numSplits = 3  totalSize = 5 goalSize = 5/3 = 1
numSlices = totalSize/goalSize = 5/1 = 5   最终会分为5个分区。

2.RDD转换算子

算子其实就是RDD对象的方法,为了和Scala集合中的方法进行区分称之为算子

RDD中的方法一般分为两大类

        1,逻辑封装:将旧的逻辑转换为新的逻辑,称之为转换算子

        2,执行逻辑:将封装好的逻辑执行,运行整个作业,称之为行动算子

2.1转换算子

2.1.1map算子

object Spark_01_RDD_Oper_Transform {

  //TODO 算子 - 转换 - map
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
    val context = new SparkContext(conf)

    val rdd: RDD[Int] = context.makeRDD(List(1, 2, 3, 4),2)

    //TODO 1.分区:新的RDD默认情况下的分区数与旧的RDD的分区数相同
    //TODO 2.数据在处理的过程中,默认分区不变
    //数据在执行的过程中,遵循执行顺序:分区内有序,分区间无序
    //map算子表示将数据源中的每一条数据进行处理
    //map算子的参数是函数类型: Int => U
    val rdd1: RDD[Int] = rdd.map(_ * 3)
    //RDD是对逻辑的封装,如果存在多个RDD对数据处理,那么每个分区的一条数据走完所有RDD后才轮到下一条数据
    val rdd2: RDD[Unit] = rdd1.map(println(_))
  }

}

利用map算子从日志数据中获取用户请求的url路径案例:

object Spark_01_RDD_Oper_Transform_1 {

  //TODO 算子 - 转换 - map
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
    val context = new SparkContext(conf)
    //从服务器日志数据apache.log中获取用户请求的URL路径
    val linesRdd: RDD[String] = context.textFile("data/apache.log")
    val urlRdd: RDD[String] = linesRdd.map(
      line => {
        val datas: Array[String] = line.split(" ")
        datas(6)
      })

    urlRdd.collect().foreach(println)
    context.stop()
  }

}

2.1.2 mapPartitions算子

mapPartitions是将数据按照分区进行批处理,效率比map算子更高,但是如果数据量大会长时间占用内存,如果内存有限的情况下,推荐使用map

object Spark_02_RDD_Oper_Transform {

  //TODO 算子 - 转换 - map
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
    val context = new SparkContext(conf)
    val rdd: RDD[Int] = context.makeRDD(List(1, 2, 3, 4), 2)

    //对整个分区的数据进行操作
    //有几个分区就执行几遍,比处理单个数据效率高(批处理)
    val rdd1: RDD[Int] = rdd.mapPartitions(
      list => list.map(_ * 2)
    )
  }
}

2.1.3mapPartitionsWithIndex算子

mapPartitionsWithIndex算子的参数列表有两个参数,分别为index,Iterator,返回值为Iterator,可以根据参数对指定分区的数据进行处理。

object Spark_03_RDD_Oper_Transform {

  //TODO 算子 - 转换 - map
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
    val context = new SparkContext(conf)
    val rdd: RDD[Int] = context.makeRDD(List(1, 2, 3, 4,5,6), 3)

    //获取第二个分区的数据
    val rdd1: RDD[Int] = rdd.mapPartitionsWithIndex(
      (index, list) => {
        if (index == 1) {
          list
        } else {
          Nil.iterator
        }
      }
    )
    rdd1.collect().foreach(println)
  }
}

2.1.4 flatMap算子 

flatMap是将数据集中的每个独立的数据扁平化后用容器返回

object Spark_04_RDD_Oper_Transform_1 {

  //TODO 算子 - 转换 - 扁平化
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("RDD")
    val context = new SparkContext(conf)
    val rdd: RDD[String] = context.makeRDD(
      List("hello scala", "hello spark")
    )
    //flatMap扁平化
    //flatMap将数据集中每个独立的数据整体扁平化后用容器返回
    val rdd1: RDD[String] = rdd.flatMap(_.split(" "))
    rdd1.collect().foreach(println)
    context.stop()
  }
}

案例:将List(List(1,2),3,List(4,5))扁平化


                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值