初识Spark-core

RDD的概念

RDD是指弹性分布式数据集,可以把RDD当成list使用

RDD五大特性

1、RDD由一组分区组成,默认一个block块对应一个分区

2、算子是作用在每一个分区上的,每一个切片对应一个task

3、RDD之间有一系列的依赖关系,有shuffle:宽依赖,没有shuffle:窄依赖

4、分区类的算子只能作用在KV格式的RDD上[groupBykey,sortBykey,reduceBykey]

5、spark为task提供了最佳计算位置,spark会将task发送到所在的数据结点执行

缓存级别的选择

首先了解读写速度
从内存中读取数据>从磁盘中读取数据>从网络中读取数据

缓存级别的选择,常用的有两个
1、如果内存充足,数据量不大,选择MEMORY_ONLY(默认)【仅内存】

2、如果内存不能完全放下所有的数据,选择MEMORY_AND_DISK_SER【内存和磁盘并且压缩】

算子的概念

算子的分类

1、转换算子(Transformations)
返回值是RDD
转换算子是懒执行,需要一个操作算子触发执行

2、操作算子(action)
返回值不是RDD
每一个action算子会触发一个任务

转换算子

1、map:一对一

map算子: 将rdd中的数据一行一行传递给后面的函数,及那个函数的返回值构建成一个新的rdd

mapPartitions: 将一个分区的数据传递给后面的函数,
一次处理一个分区的数据,需要返回一个迭代器

为什么是迭代器而不是集合,因为集合会将数据加载到内存中,
如果一个分区数据量太大会导致内存溢出

object Demo6Map {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("map")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    /**
      * 构建rdd方法
      * 1、读取文件
      * 2、基于scala集合构建rdd --- 一班用于测试
      *
      */

    //基于scala集合构建rdd
    val rdd1: RDD[Int] = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9), 2)

    //getNumPartitions 不是一个算子,是一个普通的方法
    println(s"rdd1:${rdd1.getNumPartitions}")
    /**
      * map算子: 将rdd中的数据一行一行传递给后面的函数,及那个函数的返回值构建成一个新的rdd
      *
      */

    val rdd2: RDD[Int] = rdd1.map((i: Int) => i + 1)

    rdd2.foreach(println)


    /**
      * mapPartitions: 将一个分区的数据传递给后面的函数,
      * 一次处理一个分区的数据,需要返回一个迭代器
      *
      * 为什么是迭代器而不是集合,因为集合会将数据加载到内存中,
      * 如果一个分区数据量太大会导致内存溢出
      *
      */
    val rdd3: RDD[Int] = rdd1.mapPartitions((iter: Iterator[Int]) => {
      println("=" * 100)
      //对这个迭代器进行处理,这里是scala api
      val iterator: Iterator[Int] = iter.map((i: Int) => i * 2)
      //返回一个迭代器
      iterator
    })

    rdd3.foreach(println)

    /**
      * mapPartitionsWithIndex: 多了分区编号
      */

    val rdd4: RDD[Int] = rdd1.mapPartitionsWithIndex {
      case (index: Int, iter: Iterator[Int]) =>
        println(s"当前分区的编号:$index")

        iter
    }

    rdd4.foreach(println)
  }
}

2、filter:过滤

filter算子: 对数据进行过滤,函数返回treu保留数据,函数返回false过滤数据

object Demo7Filter {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("filter")

    conf.setMaster("local")

    val sc = new SparkContext(conf)


    val rdd1: RDD[Int] = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8, 9))

    /**
      * filter算子: 对数据进行过滤,函数返回treu保留数据,函数返回false过滤数据
      *
      */

    val filterRDD: RDD[Int] = rdd1.filter((i: Int) => {
      //保留偶数
      i % 2 == 0
    })

    filterRDD.foreach(println)

  }

}

3、flatmap:一行变多行

flatMap算子:将rdd中的数据一行一行传递给后面的函数,函数返回值必须是一个序列

flatMap会将返回的序列展开,构建成一个新的rdd

object Demo8FlatMap {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("filter")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    val rdd1: RDD[String] = sc.parallelize(List("java,spark,scala,hadoop", "hadoop,hive,hbase"))

    /**
      * flatMap算子:将rdd中的数据一行一行传递给后面的函数,函数返回值必须是一个序列
      * flatMap会将返回的序列展开,构建成一个新的rdd
      *
      */
    val rdd2: RDD[String] = rdd1.flatMap((str: String) => {
      str.split(",")
    })

    rdd2.foreach(println)
  }

}

4、sample:抽样

参数:(是否放回,抽样比列)
抽样不精确

object Demo9Sample {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("sample")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    val studentRDD: RDD[String] = sc.textFile("data/students.txt")

    /**
      * sample: 对数据进行抽样
      */

    val sampleRDD: RDD[String] = studentRDD.sample(false, 0.1)

    sampleRDD.foreach(println)
  }

}

5、groupBy&groupBykey

groupBy

指定一个分组的字段进行分组, 不需要一定是一个kv格式
返回的新的rdd的value里面保护所有的字段

groupBykey

rdd必须是一个kv格式
返回的新的rdd的迭代器中的数据只包含value, 后续在处理数据的时候方便一点,指定key进行分组,返回值只有(k,v),可以减少shuffle过程传输的数据量

object Demo10Group {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("group")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    //统计学生的总分

    val linesRDD: RDD[String] = sc.textFile("data/score.txt")

    //取出学号和分数
    val scoreRDD: RDD[(String, Int)] = linesRDD
      .map(line => line.split(","))
      .filter(arr => arr.length == 3)
      .map {
        case Array(id: String, _: String, sco: String) =>
          (id, sco.toInt)
      }

    /**
      * groupBy: 指定一个分组的字段进行分组,  不需要一定是一个kv格式
      * 返回的新的rdd的value里面保护所有的字段
      *
      */

    val groupByRDD: RDD[(String, Iterable[(String, Int)])] = scoreRDD.groupBy(kv => kv._1)


    groupByRDD
      .map {
        case (id: String, iter: Iterable[(String, Int)]) =>
          val sumSco: Int = iter.map(_._2).sum
          (id, sumSco)
      }
      .foreach(println)

    /**
      * groupByKey: rdd必须是一个kv格式
      * 返回的新的rdd的迭代器中的数据只包含value, 后续在处理数据的时候方便一点
      *
      * groupByKey: 可以减少shuffle过程中传输的数据量,效率比groupBy高
      *
      */

    val groupByKeyRDD: RDD[(String, Iterable[Int])] = scoreRDD.groupByKey()

    groupByKeyRDD
      .map {
        case (id: String, iter: Iterable[Int]) =>
          (id, iter.sum)
      }
      .foreach(println)

    while (true) {

    }
  }

}

6、ReduceByKey

ReduceByKey
对相同的key的value进行聚合计算, 需要一个聚合函数

reduceBykey会在map端进行预聚合,可以减少shuffle过程中需要传输的数据量,效率比GrouByKey高
能使用reducekeyKey的时候尽量使用reduceByKey

对同一个key的value做聚合
聚合次数=value数-1

局限性:不能处理太复杂的逻辑

使用范围:必须是K,V格式才能使用

object Demo11ReduceByKey {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("group")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    //统计学生的总分

    val linesRDD: RDD[String] = sc.textFile("data/score.txt")

    //取出学号和分数
    val scoreRDD: RDD[(String, Int)] = linesRDD
      .map(line => line.split(","))
      .filter(arr => arr.length == 3)
      .map {
        case Array(id: String, _: String, sco: String) =>
          (id, sco.toInt)
      }

    /**
      * groupByKey: 先分组,分组之后对对value进行聚合计算
      *
      */
    scoreRDD
      .groupByKey()
      .map(kv => (kv._1, kv._2.sum))
      .foreach(println)

    /**
      * reduceByKey: 对相同的key的value进行聚合计算, 需要一个聚合函数
      *
      * reduceBykey会在map端进行预聚合,可以减少shuffle过程中需要传输的数据量,效率比GrouByKey高
      * 能使用reducekeyKey的时候尽量使用reduceByKey
      *
      */

    val countRDD: RDD[(String, Int)] = scoreRDD.reduceByKey((x: Int, y: Int) => {
      val j: Int = x + y
      //println(s"$x + $y = $j")
      j
    })

    countRDD.foreach(println)

    while (true) {

    }
  }

}

7、union

合并两个rdd,不会对数据做去重,两个rdd的数据类型要完全一致,在物理层面没有合并,只是在逻辑层面合并了

object Demo13Union {
  def main(args: Array[String]): Unit = {


    val conf = new SparkConf()

    conf.setAppName("union")

    conf.setMaster("local")

    val sc = new SparkContext(conf)


    val rdd1: RDD[Int] = sc.parallelize(List(1, 2, 3, 4, 5, 6))
    val rdd2: RDD[Int] = sc.parallelize(List(3, 4, 5, 6, 7, 8, 9))

    println(s"rdd1:${rdd1.getNumPartitions}")
    println(s"rdd2:${rdd2.getNumPartitions}")


    /**
      * union :合并两个rd, 不会对数据做去重, 两个rdd的类型要完全一致
      * 在物理层面并没有合并,只是在逻辑层面合并了
      */
    val unionRDD: RDD[Int] = rdd1.union(rdd2)

    println(s"unionRDD:${unionRDD.getNumPartitions}")

    unionRDD.foreach(println)


  }

}

8、join

join分类

join:内连接,取出左右两个表都有的数据

leftOuterjoin:左连接,以左表为基础,如果右表没有这个数据补None

rightOuterjoin:右连接,以右表为基础,如果左表没有这个数据补None

fullOuterjoin:全连接,左右两个并集的数据,没有数据的补None

join

object Demo14Join {
  def main(args: Array[String]): Unit = {


    val conf = new SparkConf()

    conf.setAppName("union")

    conf.setMaster("local")

    val sc = new SparkContext(conf)


    val idNameRDD: RDD[(String, String)] = sc.parallelize(List(
      ("000", "晓伟"),
      ("001", "张三"),
      ("002", "李四"),
      ("003", "王五"))
    )
    val idAgeRDD: RDD[(String, Int)] = sc.parallelize(List(
      ("001", 23),
      ("002", 24),
      ("003", 25),
      ("004", 23))
    )

    /**
      * innerJoin: 两个表都有才能关联上
      *
      */

    val innerJoinRDD: RDD[(String, (String, Int))] = idNameRDD.join(idAgeRDD)

    //整理数据
    innerJoinRDD
      .map {
        case (id: String, (name: String, age: Int)) =>
          (id, name, age)
      }
      .foreach(println)


    /**
      * leftOuterJoin: 以左表为基础,如果右表没有这个key,补NOne
      *
      * Option: 可选择的值,有值或者没有值
      */


    val leftJoinRDD: RDD[(String, (String, Option[Int]))] = idNameRDD.leftOuterJoin(idAgeRDD)

    //整理数据
    leftJoinRDD
      .map {

        //关联上的处理方式
        case (id: String, (name: String, Some(age))) =>
          (id, name, age)

        //没有关联上的处理方式
        case (id: String, (name: String, None)) =>
          //给年龄一个默认值
          (id, name, 0)

      }
      .foreach(println)

    /**
      *
      * fullOuterJoin: 以两个表为基础,有一边有数据就会出来结果,列一边补None
      *
      */


    val fullJoinRDD: RDD[(String, (Option[String], Option[Int]))] = idNameRDD.fullOuterJoin(idAgeRDD)


    fullJoinRDD
      .map {
        case (id: String, (Some(name), None)) =>
          (id, name, 0)

        case (id: String, (None, Some(age))) =>
          (id, "默认值", age)

        case (id: String, (Some(name), Some(age))) =>
          (id, name, age)
      }
      .foreach(println)
  }

}

9、mapvalues

key不变,只对values做处理

object Demo15MapValues {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf()

    conf.setAppName("union")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    val idAgeRDD: RDD[(String, Int)] = sc.parallelize(List(
      ("001", 23),
      ("002", 24),
      ("003", 25),
      ("004", 23))
    )

    /**
      * mapValues: key不变,对value做处理
      *
      */
    val rdd: RDD[(String, Int)] = idAgeRDD.mapValues(age => age + 1)

    rdd.foreach(println)
  }

}

10、sortBy&sortBykey

sortBy: 指定一个字段进行排序
sortByKey;通过key进行排序
默认是升序

object Demo16Sort {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("group")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    val linesRDD: RDD[String] = sc.textFile("data/score.txt")

    //取出学号和分数
    val scoreRDD: RDD[(String, Int)] = linesRDD
      .map(line => line.split(","))
      .filter(arr => arr.length == 3)
      .map {
        case Array(id: String, _: String, sco: String) =>
          (id, sco.toInt)
      }

    //计算总分
    val sumScoreRDD: RDD[(String, Int)] = scoreRDD.reduceByKey(_ + _)
    /**
      * sortBy: 指定一个字段进行排序
      * sortByKey;通过key进行排序
      * 默认是升序
      *
      */

    val sortByRDD: RDD[(String, Int)] = sumScoreRDD.sortBy(kv => kv._2, ascending = false)
    sortByRDD.foreach(println)

  }

}

11、distinct:去重

distinct: 对数据去重,会产生shuffle

object Demo17Distinct {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()

    conf.setAppName("group")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    val rdd1: RDD[Int] = sc.parallelize(List(1, 2, 3, 4, 5, 7, 87, 9, 4, 4, 3, 2, 4, 5, 6))

    /**
      * distinct: 对数据去重,会产生shuffle
      *
      */
    val distinctRDD: RDD[Int] = rdd1.distinct()

    distinctRDD.foreach(println)
  }

}

操作算子(action)

action算子,每一个action算子都会产生一个job

1、count

统计RDD数据的行数

sql中count(1)与count(*)的区别
count(1) ,这一行分组的字段都为null不计数
count(*) ,这一行所有的都为null不计数

2、sum

对rdd中的数据求和,rdd中的数据类型必须是数字

3、take

取top, 返回一个数组, 不能取太多, 会导致内存溢出

4、collect

将rdd转换成数组,如果rdd数据量比较大,会导致内存溢出 (1G)

5、foreach

遍历rdd中的数据,也是一个action算子

6、foreachPartition

一次将一个分区的数据传递给后面的函数

7、saveAsTextFile

将数据保存在hdfs中

object Demo18Action {
  def main(args: Array[String]): Unit = {

    val conf = new SparkConf()

    conf.setAppName("group")

    conf.setMaster("local")

    val sc = new SparkContext(conf)

    val studentRDD: RDD[String] = sc.textFile("data/students.txt")

    /**
      * action算子,每一个action算子都会产生一个job
      *
      */

    /**
      * count: 统计rdd的数据行数
      */
    val count: Long = studentRDD.count()

    println(count)

    /**
      * sum: 对rdd中的数据求和,rdd中的数据类型必须是数字
      *
      */

    val sumAge: Double = studentRDD
      .map(line => line.split(",")(2).toInt)
      //对所有数据求和,只能是数字类型
      .sum()

    println(sumAge / count)


    /**
      * take: 取top, 返回一个数组, 不能取太多, 会导致内存溢出
      *
      */

    val top: Array[String] = studentRDD.take(10)

    top.foreach(println)

    /**
      * collect: 将rdd转换成数组,如果rdd数据量比较大,会导致内存溢出  (1G)
      */


    val array: Array[String] = studentRDD.collect()

    array.foreach(println)


    /**
      * foreach: 遍历rdd中的数据,也是一个action算子
      * foreachPartition: 一次将一个分区的数据传递给后面的函数
      *
      */

    studentRDD.foreach(println)


    studentRDD.foreachPartition((iter: Iterator[String]) => {
      iter.foreach(println)
    })

    /**
      * saveAsTextFile: 将数据保存在hdfs中
      *
      */

    HDFSUtil.deletePath("data/test")
    studentRDD.saveAsTextFile("data/test")


    while (true) {}
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值