随笔 - 39  文章 - 0  评论 - 0  阅读 - 4234

Spark API

Spark API

创建spark环境

方法一:SparkConf

//spark环境配置对象
val conf = new SparkConf()

//设置spark任务的名称
conf.setAppName("Demo1WordCount")

//设置spark运行模式,local:本地运行
conf.setMaster("local")

//创建spark上下文对象,sc是spark写代码的入口
val sc = new SparkContext(conf)

方法二:SparkSession (这不仅是一个spark新版本的入口,还是spark sql 的入口

val spark: SparkSession = SparkSession
  .builder()
  .master("local")
  .appName("sql")
  .getOrCreate()
  val sc = new SparkContext(spark)

RDD

RDD:弹性的分布式数据集,是分布式内存的一个抽象概念,RDD提供了一种高度受限的共享内存模型,即RDD是只读的记录分区的集合,只能通过在其他RDD执行确定的转换操作(如map、join和group by)而创建,然而这些限制使得实现容错的开销很低。对开发者而言,RDD可以看作是Spark的一个对象,它本身运行于内存中,如读文件是一个RDD,对文件计算是一个RDD,结果集也是一个RDD ,不同的分片、 数据之间的依赖 、key-value类型的map数据都可以看做RDD。

它具备像MapReduce等数据流模型的容错特性,并且允许开发人员在大型集群上执行基于内存的计算。

有关RDD的基本操作

从spark上下文对象的textFile方法可以获取文件中数据的RDD

//如果是在集群运行,就是读取hdfs的文件
val linesRDD: RDD[String] = sc.textFile("data/words.txt")

小例:WordCount

object Demo1WordCount {
  def main(args: Array[String]): Unit = {
    /**
     * 1、创建spark环境
     *
     */

    //spark环境配置对象
    val conf = new SparkConf()

    //设置spark任务的名称
    conf.setAppName("Demo1WordCount")

    //设置spark运行模式,local:本地运行
    conf.setMaster("local")

    //创建spark上下文对象,sc是spark写代码的入口
    val sc = new SparkContext(conf)


    /**
     * 2、读取文件
     * spark读取文件底层的代码和MapReduce是一样的
     * 所以切片规则一样
     * spark是一个切片对应一个分区
     */
    //如果是在集群运行,就是读取hdfs的文件
    val linesRDD: RDD[String] = sc.textFile("data/words.txt")


    /**
     * 3、将单词展开
     *
     */

    val wordsRDD: RDD[String] = linesRDD.flatMap((line: String) => line.split(","))


    /**
     * 4、按照单词分组
     *
     */
    val kvRDD: RDD[(String, Iterable[String])] = wordsRDD.groupBy((word: String) => word)

    /**
     * 5、统计单词的数量
     *
     */

    val wordCount: RDD[String] = kvRDD.map {
      case (word: String, iter: Iterable[String]) =>
        val count: Int = iter.size
        s"$word\t$count"
    }

    /**
     * 6、保持数据
     *
     */

    wordCount.saveAsTextFile("data/wordcount")

  }

}

例子中使用了多种RDD处理数据的方法:flatMap,groupBy,map等

map

map:将rdd的数据一条一条传递给后面的函数,函数的返回值构建成一个新的RDD
map算子不会改变总的数据行数

val clazzRDD: RDD[String] = studentsRDD.map((student: String) => student.split(",").last)
//将获得的字段进行split切分并取最后一个字段传给新的RDD

map中需要一个表达式,即map要对每一行数据进行的操作,并把结果传递给下一个RDD

上述例子中(student: String) => student.split(",").last就是一个函数

flatMap

flatMap:一条一条将RDD的数据传递给后面的函数,函数的返回值必须是一个集合,最后会将集合展开构建成一个新的RDD

传入一行返回多行

val wordsRDD: RDD[String] = linesRDD.flatMap(line => line.split(","))
//将linesRDD中的数据一行一行传入,将每行数据逗号分隔将分隔后的数据传给wordsRDD

groupBy

groupBy: 按照指定的字段进行分组,返回一个kv格式的RDD,key是分组的字段,value是一个迭代器
迭代器的数据没有完全加载的内存中,迭代器只能迭代一次

groupBy算子需要将相同的key分到同一个分区中,所有会产生shuffle

val kvRDD: RDD[(String, Iterable[(String, Int)])] = clazzAndAgeRDD.groupBy(kv => kv._1)
//将clazzAndAgeRDD按照kv中第一个字段进行分组获得一个新的RDD:kvRDD

新的RDD分为两个部分,第一个部分为分组字段,第二个部分为分组后每一组的字段集合是一个迭代器

新的RDD可以对分组后的内容进一步整合,比如:求和,求个分组内的数量,求分组内最大值等等

groupByKey

groupByKey:按照key进行分组

val groupByKeyRDD: RDD[(String, Iterable[Int])] = clazzAndAgeRDD.groupByKey()
//groupByKey只能对kv类型的RDD进行分组,并且分组字段为key,可以得到一个由key和一个迭代器组成的元组的RDD,其中迭代器中只包含原分组中的value,不同于groupBy中包含整个元数据

groupBy和groupByKey的区别:

1、代码:groupBy可以在任何类型的rdd上使用,groupByKey只能作用在kv格式的RDD上
2、groupByKey之后RDD的结构相对简单一点
3、性能:groupByKey shuffle过程需要传输的数据量比groupBy小,性能更高

filter

filter:将RDD的数据一条一条传递给函数,如果函数返回true保留数据,如果函数返回false过滤数据
filter会减少RDD的数据行数

val filterRDD: RDD[String] = studentsRDD.filter((student: String) => {
  val gender: String = student.split(",")(3)

  "男".equals(gender)
})

filter将RDD通过传入的函数进行过滤,其中函数的返回值类型为Boolean

上述例子中就是将传入的每一行字段以","切分,将第4个字段中为"男"的整个字段传给新的RDD

union

union:合并两个RDD,两个rdd的类型要一致,不会对数据去重
union只是在逻辑层面合并了,物理层面没有合并
合并之后新的rdd的分区数等于墙面两个rdd的和

val unionRDD: RDD[String] = rdd1.union(rdd2)

join

先举一个例子:

object Demo11Join {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setAppName("map")
    conf.setMaster("local")

    val sc = new SparkContext(conf)

    //基于集合构建一个rdd,用于测试
    val nameRDD: RDD[(String, String)] = sc.parallelize(
      List(
        ("001", "张三"),
        ("002", "李四"),
        ("003", "王五"),
        ("004", "赵六")
      ))

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

    /**
     * inner join :内关联,两边都有才能关联上
     *
     */

    val innerJoinRDD: RDD[(String, (String, Int))] = nameRDD.join(ageRDD)

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


    /**
     * left join :左关联,以左表为基础,如果右表没有数据,补null
     *
     * Option: 两个取值,有值或者None, 如果没有关联上,就是None
     *
     */

    val leftJoinRDD: RDD[(String, (String, Option[Int]))] = nameRDD.leftOuterJoin(ageRDD)

    //整理数据
    leftJoinRDD
      .map {
        //关联上的情况
        case (id: String, (name: String, Some(age))) =>
          (id, name, age)

        //没有关联上的处理方式
        case (id: String, (name: String, None)) =>
          (id, name, 0)
      }
      .foreach(println)

    /**
     * full join : 全关联,只要有一边有数据就会出结果,如果另一边没有,补null
     *
     */

    val fullJoinRDD: RDD[(String, (Option[String], Option[Int]))] = nameRDD.fullOuterJoin(ageRDD)

    //整理数据
    fullJoinRDD
      .map {
        case (id: String, (Some(name), Some(age))) =>
          (id, name, age)

        case (id: String, (Some(name), None)) =>
          (id, name, 0)

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

inner join :内关联,两边都有才能关联上

left join :左关联,以左表为基础,如果右表没有数据,补null

Option: 两个取值,有值或者None, 如果没有关联上,就是None

full join : 全关联,只要有一边有数据就会出结果,如果另一边没有,补null

sortBy

sortBy: 指定一个字段进行排序,默认是升序
ascending = false: 降序

val sortRDD: RDD[(String, Int)] = sumScoRDD.sortBy(kv => kv._2, ascending = false)
//按照第二个字段降序排序

mapValue

mapValues: 对value做处理,可以不变

val mapValuesRDD: RDD[(String, Int)] = sumScoRDD.mapValues(scp => scp / 10)
posted on   +1000  阅读(388)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示