Spark(二)Spark Core

大数据—— Spark Core 知识点整理 1. Spark 和 Hadoop 相比有什么优势 运行速度快: Spark拥有DAG执行引擎,支持在内存中对数据进行迭代计算。官方提供的数据表明,如果数据由磁盘读取,速度是Hadoop MapReduce的10倍以上,如果数据从内存中读取,速度可以高达100多倍。 适用场景广泛: 大数据分析统计,实时数据处理,图计算及机器学习 易用性: 编写简单,支持80种以上的高级算子,支持多种语言,数据源丰富,可部署在多种集群中 容错性高: ... 阅读详情

一.RDD

1.概念

RDD:弹性分布式数据集,是Spark中最基本的数据抽象,用来表示分布式集合,支持分布式操作,代表一个不可变、只读的,被分区的数据集

弹性

  • 存储的弹性:内存与磁盘的自动切换

  • 容错的弹性:数据丢失可以自动恢复

  • 计算的弹性:计算出错重试机制

  • 分片的弹性:可根据需要重新分片

分布式:数据存储在大数据集群不同节点上

数据集:RDD封装了计算逻辑,并不保存数据,读取数据后会经过一系列的计算逻辑直到将结果保存或显示

数据抽象:RDD是一个抽象类,需要子类具体实现

不可变:RDD封装了计算逻辑,是不可以改变的,想要改变,只能产生新的RDD,在新的RDD里面封装计算逻辑

可分区、并行计算

RDD五大属性

1)A list of partitions

RDD 数据结构中存在分区列表,用于执行任务时并行计算,是实现分布式计算的重要属性

2)A function for computing each split

Spark 在计算时,是使用分区函数对每一个分区进行计算

3)A list of dependencies on other RDDs

RDD 是计算模型的封装,当需求中需要将多个计算模型进行组合时,就需要将多个 RDD 建立依赖关系

4)Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned)

当数据为KV 类型数据时,可以通过设定分区器自定义数据的分区

5)Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)

一组最优的数据块的位置,这里涉及到数据的本地性和数据位置最优spark后期在进行任务调度的时候,会优先考虑存有数据的worker节点来进行任务的计算。大大减少数据的网络传输,提升性能

2.RDD转换算子
2.1 RDD两类算子

Transformation:返回一个新的RDD,它不会立即执行,需要Action函数触发

Action:返回值不是RDD,它会立即执行

在这里插入图片描述

2.2 RDD创建

RDD中的数据可以来源于2个地方:本地集合或外部数据源

  • sc.parallelize(本地集合,分区数)
  • sc.makeRDD(本地集合,分区数) 底层是parallelize
  • sc.textFile(本地/HDFS/文件夹,分区数) 以行为单位读取数据
  • sc.wholeTextFiles(本地/HDFS/文件夹,分区数) 以文件为单位,专门读取小文件,结果是元组,第一个元素是文件路径,第二个元素是文件内容
2.3 RDD分区

Spark RDD的分区规则详解

  • rdd.getNumPartitions 获取分区数,底层是partitions.length
  • rdd.partitions.length 获取分区数
object RddCreate {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val list = List[String]("liu", "guan", "zhang")
    val list2 = ListBuffer[Int]() // 可变List
    list2.append(1,2,3,10,18)

    val rdd: RDD[String] = sc.parallelize(list, 2)

    val rdd2: RDD[Int] = sc.makeRDD(list2, 3) // 底层是parallelize

    val rdd3: RDD[String] = sc.textFile("hdfs://192.168.10.102:8020/input/spark")

    val rdd4: RDD[(String, String)] = sc.wholeTextFiles("F:\\input\\small",1)

    val partitions: Int = rdd.getNumPartitions
    println(partitions)

    val partitions1: Int = rdd2.getNumPartitions
    println(partitions1)

    val partitions2: Int = rdd3.getNumPartitions
    println(partitions2)

    val partitions3: Int = rdd4.getNumPartitions
    println(partitions3)

  }
}

在这里插入图片描述

2.4 RDD Value类型算子

map是对RDD中每个分区的数据逐条进行自定义转换

flatMap是对数据先映射后扁平化,返回TraversableOnce

mapPartitions则是对RDD中的每个分区的迭代器进行操作,数据已经在迭代器里,返回一个迭代器

map 算子因为类似于串行操作,所以性能比较低,而是 mapPartitions 算子类似于批处理,所以性能较高。但是mapPartitions 算子会长时间占用内存,那么这样会导致内存可能不够用,出现内存溢出的错误。所以在内存有限的情况下,不推荐使用。使用map操作

mapPartitionsWithIndex是对每个分区的迭代器进行操作,并且具有分区号

综合示例

object RddPartition {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    val list: ListBuffer[Map[Int, Tuple2[String,Int]]] =
      new ListBuffer[Map[Int, Tuple2[String,Int]]]()

    val lines: RDD[String] = sc.textFile("F:\\input\\wordcount",2)

    // flatMap扁平化映射,每一行以_分割后组成一个集合,最后将所有集合再整合为一个集合
    lines.flatMap(_.split(" "))
      // mapPartitions以分区为单位进行操作
      .mapPartitions(iter =>{
        // map映射
        iter.map((_,1))
      })
      .reduceByKey(_ + _)
      .mapPartitionsWithIndex((index, iter) => {
        iter.foreach(value => {
          val data = Map[Int, Tuple2[String, Int]](index -> value)
          list += data
        })
        list.iterator
      }).foreach(map => println(map))
  }
}

在这里插入图片描述

glom将同一个分区的数据直接转换为相同类型的内存数组进行处理,分区不变

groupBy根据一个函数的规则进行分组

filter将数据根据指定的规则进行筛选过滤,符合规则的数据保留,不符合规则的数据丢弃

sample根据指定的规则从数据集中抽取数据

distinct去重

repartition可以增加或减少分区,输入几分区数就变为几。注意:原来的RDD不变,因为产生了新的RDD

coalesce(Int,shuffle) 默认减少分区,如果把shuffle参数指定为true,数据将会被打乱重新分区,并且具有了扩大分区的功能,否则原来分区的数据不会被分开,且只能缩小分区。注意:原来的RDD不变,因为产生了新的RDD

sortBy 每个分区内单独排序

object RddValue {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val list: ListBuffer[Int] = new ListBuffer[Int]() // 可变List
    list.append(1,2,3,4,5,6,7,8,8,8,6)

    val rdd: RDD[Int] = sc.parallelize(list,3)

    // glom转换成一个类型相同的数组
    val value: RDD[Array[Int]] = rdd.glom()

    // gruopBy(function)
    val value1: RDD[(Boolean, Iterable[Int])] = rdd.groupBy(num => {
      num < 5    // 根据num小于5分组
    })
    value1.foreach(data => {
      println(data._2)
    })

    // filter根据条件过滤 RDD中小于4的元素
    rdd.filter(num => {
      num < 4
    }).foreach(println)

    // sample根据指定的规则从数据集中抽取数据
    // false代表每个数据被抽到不放回,0.3代表每个数据被抽取到的概率
    // true代表每个数据被抽到放回,第二个参数代表期望被抽到的次数
    rdd.sample(false,0.3)

    // distinct去重
    rdd.distinct().foreach(println)
    // sortBy 每个分区内单独排序,第二个参数控制升降序
    rdd.sortBy(num =>{
      num
    },false).saveAsTextFile("./sort")

  }
}
2.5 RDD 双Value类型算子

就是要求有两个RDD集合参与运算

object RddValues {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val list: ListBuffer[Int] = new ListBuffer[Int]() // 可变List
    list.append(1,2,3,4,5,6,7,8,8,8,6)
    val list1: List[Int] = List[Int](6, 8, 5, 10, 40, 60, 100,8,200,123,56)

    val rdd: RDD[Int] = sc.parallelize(list)
    val rdd1: RDD[Int] = sc.parallelize(list1)

    // RDD交集,数据类型要求相同
    val intersection: RDD[Int] = rdd.intersection(rdd1)

    // 并集,数据类型要求相同
    val union: RDD[Int] = rdd.union(rdd1)

    // 差集,不同角度结果不同,数据类型要求相同
    val subtract: RDD[Int] = rdd.subtract(rdd1)

    // 拉链配对,要求个数相等,分区数量相等,数据类型可以不一致
    val zip: RDD[(Int, Int)] = rdd.zip(rdd1)
    zip.foreach(println)
  }
}
2.6 RDD K-V类型算子

partitionBy将数据按照指定Partitioner 重新进行分区。Spark 默认的分区器是HashPartitioner

reduceByKey可以将数据按照相同的Key对Value进行聚合

groupByKey将数据源的数据根据key对value进行分组

面试:groupByKey和reduceByKey区别

groupByKey和reduceByKey是以Key为单位的,但是相同的Key可能在不同的分区,于是就有落盘操作,先在磁盘上将所有相同的Key聚集到一个分区,再读入内存

groupByKey不会在每个分区预聚合,只有等待sum时才聚合,集群节点之间传输的开销较大。groupByKey 只能分组,不能聚合
在这里插入图片描述
reduceByKey会在每个分区预先聚合,在落盘,读取文件效率高,集群节点之间传输的开销较小。reduceByKey其实包含分组和聚合的功能
在这里插入图片描述
aggregateByKey将数据根据不同的规则进行分区内计算和分区间计算,初始值只会参与分区内计算

(zeroValue)((zeroValue,key的value值) => {key的value值聚合进zeroValue})((t1,t2) => 分区间聚合)

foldByKey当分区内计算规则和分区间计算规则相同时,aggregateByKey 就可以简化为foldByKey

combineByKey
createCombiner: V => C,相同key的第一条数据处理
mergeValue: (C, V) => C 分区内处理函数
mergeCombiners: (C, C) => C // 分区间处理函数

object ByKey {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val rdd = sc.makeRDD(List(("a",5),("a",4),("c",7),("b",10),("b",2),("c",11)),2)
    // reduceByKey
    rdd.reduceByKey((a,b) =>a+b).foreach(println)
    // foldByKey  求各个键对应的最大Value值
    rdd.foldByKey(0)((a,b) => Math.max(a,b)).foreach(println)

    // aggregateByKey
    rdd.aggregateByKey((0,0))(
      (t,v) => { // t代表(0,0) v代表5
        (t._1 + v,t._2 + 1)
      },
      (t1,t2) =>{
        (t1._1 + t2._1,t2._2 + t2._2)
      }
    ).map(data =>{
      (data._1, data._2._1 /data._2._2)
    }).foreach(println)
    // combineByKey 
    // createCombiner: V => C,相同key的第一条数据处理
    // mergeValue: (C, V) => C 分区内处理函数
    // mergeCombiners: (C, C) => C // 分区间处理函数
    rdd.combineByKey(
      v => (v,1), 
      (t:(Int,Int),v) => { // t代表(5,1) v代表4
        (t._1 + v,t._2 + 1)
      },
      (t1:(Int,Int),t2:(Int,Int)) =>{
        (t1._1 + t2._1,t2._2 + t2._2)
      }
    ).map(data =>{
      (data._1, data._2._1 /data._2._2)
    }).foreach(println)
    
  }
}
2.7 RDD关联算子

类似于SQL的连接操作

object RddJoin {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)

    val empRDD: RDD[(Int, String)] = sc.parallelize(List((101, "jack"), (102, "rose"), (103, "young")))

    val deptRDD: RDD[(Int, String)] = sc.parallelize(List((101, "销售部"), (102, "财务部")))

    val result: RDD[(Int, (String, String))] = empRDD.join(deptRDD)  // 根据key匹配,类比SQL内连接
    result.foreach(data => {
      println("编号为"+data._1+"的姓名为"+data._2._1+"其部门为"+data._2._2)
    })

    val result1: RDD[(Int, (String, Option[String]))] = empRDD.leftOuterJoin(deptRDD) // 类似左连接
    result1.foreach(data => {
      println("编号为"+data._1+"的姓名为"+data._2._1+"其部门为"+data._2._2)
    })
  }
}

在这里插入图片描述
在这里插入图片描述

2.8 RDD排序算子
  • sortBy
  • sortByKey
  • top

案例:统计文本中单词数量最多的前三个

object RddSort {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)

    val lines: RDD[String] = sc.textFile("F:\\input\\wordcount",3)

    val result: RDD[(String, Int)] = lines.flatMap(_.split(" "))
      .filter(_.length >= 4)
      .map((_, 1))
      .reduceByKey(_ + _)

    // sortBy,根据数量进行排序,false降序,take取出数量
    val array: Array[(String, Int)] = result.sortBy(_._2, false).take(3)
    array.foreach(data => println(data._1+"的数量是"+data._2))

    // sortByKey 根据Key排序  但是我们目前的K-V是 (word,数量) 我们需要交换一下K-V 让数量成为K
    result.map(_.swap).sortByKey(false).take(3)
      .foreach(data =>println(data._2+"的数量是"+data._1))

    // top 默认降序,适合结果数据量很小的时候
    result.top(3)(Ordering.by(_._2)).foreach(data =>println(data._1+"的数量是"+data._2))
  }
}

所有结果均为
在这里插入图片描述

3.RDD行动算子

redcue整形聚合

collect将不同分区的数据按照顺序采集到Driver,形成数组

count 数据源的数据个数

first 数据源的第一个

take 获取前n个数据

takeOrdered 自动排序!取前n个,只适合单个value

object Action_Demo1 {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(8,2,3,4,5,8,7,9,6))
    // redcue
    val redcue: Int = rdd.reduce(_+_)
    // collect将不同分区的数据按照顺序采集到Driver,形成数组
    val ints: Array[Int] = rdd.collect()
    // count 数据源的数据个数
    val count: Long = rdd.count()
    // first  数据源的第一个
    val fir: Int = rdd.first()
    // take 获取前n个数据
    val take: Array[Int] = rdd.take(3)
    take.foreach(println)
    // takeOrdered 自动排序!取前n个,只适合单个value
    val order: Array[Int] = rdd.takeOrdered(3)
    order.foreach(println)
    
    sc.stop();
  }
}

fold(zeroValue: T)(op: (T, T) => T)
对每一个分区,执行一遍操作,初始值也要参与,最后再对每一个分区的结果执行操作,初始值再一次参与

aggregate(zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U)
对每一个分区,执行第一个函数的操作,初始值参与。然后对每一个分区的结果执行第二个函数,初始值再参与一次

countByKey 统计K-V中每个K出现的次数

conutByValue 注意,这个算子是把RDD集合中每个元素看成一个整体!即使是K-V也会把K-V看成一个整体,计算出现的次数!此value非K-V中value的意思!

object Action_Demo2 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val value: RDD[String] = sc.textFile("F:\\input\\wordcount", 3)

    // 每一个单词都是K-V键值对,(K,1)
    val wordAndOne: RDD[(String, Int)] = value.flatMap(_.split(" ")).map((_, 1))

    // countByKey  统计K-V中每个K出现的次数
    val map: collection.Map[String, Long] = wordAndOne.countByKey()
    map.foreach(println)
    // conutByValue 
    val map1: collection.Map[(String, Int), Long] = wordAndOne.countByValue()
    map1.foreach(println)
    // foldBuKey 每个值都要加上初始值
    /**
     * 因为设置了三个分区,foldByKey会以每个分区为单位,与初始值进行运算
      分区0 zhang 3+2=5    liu 1+2=3
      分区1 hao 1+2=3     wang 1+2=3      song 2+2=4
      分区2 kun 2+2=4      li 1+2=3           song 1+2=3
      再将分区结果根据Key聚合就得到了如下的结果
     */
    val result3: RDD[(String, Int)] = wordAndOne.foldByKey(2)(_ + _)
    result3.foreach(println)

    // aggregateByKey
    /**
     * 因为设置了三个分区,aggregateByKey会以每个分区为单位,根据第一个函数与初始值进行运算

      分区0 zhang 3+2=5    liu 1+2=3
      分区1 hao 1+2=3     wang 1+2=3      song 2+2=4
      分区2 kun 2+2=4      li 1+2=3           song 1+2=3

      根据分区结果,再根据第二个函数根据Key进行运算
      因为只有1个zhang,第二个函数不执行,但是song有两个,所以分区1的song会和分区2的song进行运算(第二个函数)也就是 song : 3*4 = 12
     */
    val result4: RDD[(String, Int)] = wordAndOne.aggregateByKey(2)(_ + _, _ * _)
    result4.foreach(println)
  }
}

saveAsTextFile保存到文件

saveAsObjectFile对象文件是将对象序列化后保存的文件,采用 Java 的序列化机制

collect把各分区的数据收集到本地

4.RDD序列化

闭包检测

从计算的角度,算子以外的代码都是在Driver 端执行,算子里面的代码都是在Executor 端执行。那么在 scala 的函数式编程中,就会导致算子内经常会用到算子外的数据,这样就形成了闭包的效果,如果使用的算子外的数据无法序列化,就意味着无法传值给Executor端执行,就会发生错误,所以需要在执行任务计算前,检测闭包内的对象是否可以进行序列化,这个操作我们称之为闭包检测。Scala2.12 版本后闭包编译方式发生了改

object Action_Demo3 {


  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val value: RDD[Int] = sc.parallelize(List(1,2,3,1,4))
    val user: User = new User

    value.foreach(str =>{
     println(str + user.name)
    })

  }
  class User() extends Serializable {
    var name = "对象内变量"
  }
}

如果对象不序列化,将会报错,因为无法在服务器之间进行传输

5.RDD依赖
5.1 血缘关系

RDD只支持粗粒度转换,即在大量记录上执行的单个操作。将创建 RDD 的一系列Lineage(血统)记录下来,以便恢复丢失的分区。RDD 的Lineage 会记录RDD 的元数据信息和转换行为,当该RDD 的部分分区数据丢失时,它可以根据这些信息来重新运算和恢复丢失的数据分区

通过toDebugString方法查看RDD的血缘关系

object WordCount {
  def main(args: Array[String]): Unit = {
    // 1.准备SparkContext上下文执行环境
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)

    val lines: RDD[String] = sc.textFile("F:\\input\\wordcount")
    println(lines.toDebugString)

    val words: RDD[String] = lines.flatMap(_.split(" "))
    println(words.toDebugString)

    val wordAndOne: RDD[(String, Int)] = words.map((_,1))
    println(wordAndOne.toDebugString)

    val result: RDD[(String, Int)] = wordAndOne.reduceByKey(_+_)
    println(result.toDebugString)

    result.foreach(println)

    sc.stop()
  }

}

在这里插入图片描述

5.2 宽依赖与窄依赖

窄依赖表示每一个父(上游)RDD 的Partition 最多被子(下游)RDD 的一个 Partition 使用,源码称之为OneToOne依赖

正是因为每个RDD操作的数据不变,所以我们最后只需要两个任务就可以

在这里插入图片描述

宽依赖表示同一个父(上游)RDD 的 Partition 被多个子(下游)RDD 的 Partition 依赖,会引起 Shuffle,称之为Shuffle依赖

由于宽依赖会导致数据Shuffle,所以对一个RDD的一步处理而言,代表一个阶段,对每个分区而言,都要有对应的任务进行处理,并且要等阶段内的任务全部处理完成,才可以进行下一个阶段

当RDD存在Shuffle依赖时,阶段就会增加一个。ResultStage只有一个,最开始就会创建,代表最后需要执行的阶段在这里插入图片描述

RDD 任务划分

RDD 任务切分中间分为:Application、Job、Stage 和 Task

  • Application:初始化一个 SparkContext 即生成一个Application
  • Job:一个Action 算子就会生成一个Job
  • Stage:Stage 等于宽依赖(ShuffleDependency)的个数加 1
  • Task:一个 Stage 阶段中,最后一个RDD 的分区个数就是Task 的个数
6.RDD持久化
6.1 RDD缓存

在Spark中有些RDD可能会被频繁使用到,我们可以对RDD进行持久化/缓存,这样下次再使用这些RDD时就不需要计算了,提高了效率

在这里插入图片描述

API

  • rdd.cache()
  • rdd.persist()
  • rdd.unpersist() // 清空缓存

cache底层是persist()
在这里插入图片描述

默认使用只使用内存(缓存到JVM的堆内存中)的级别
在这里插入图片描述
存储策略如下
在这里插入图片描述
开发中常用MEMORY_AND_DISK级别,内存不够放到磁盘

object RddCache {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)

    val lines: RDD[String] = sc.textFile("F:\\input\\wordcount")

    val result: RDD[(String, Int)] = lines.flatMap(_.split(" "))
      .filter(_.length >= 4)
      .map((_, 1))
      .reduceByKey(_ + _)

    // result计算过程稍微复杂,可以放到缓存中
    result.persist(StorageLevel.MEMORY_AND_DISK)
  }
}
6.2 RDD Checkpoint

RDD虽然可以缓存/持久化,但是放在内存中,遇到突然断电的情况,显然是不可靠的,即使放在磁盘上,也不能保证100%可靠

Checkpoint就是一种更加可靠的数据持久化,它将数据放在了高可靠、高容错的HDFS上,实现了RDD的高可靠和高容错

对计算复杂且后续会被频繁使用的RDD进行checkpoint

API

  • sc.setCheckpointDir(HDFS目录)
  • rdd.checkpoint

缓存/持久化和Checkpoint检查点的区别

1.存储位置

缓存/持久化数据存默认存在内存,一般设置为内存+磁盘(普通磁盘),作业执行完成磁盘文件(临时文件)就被清除了

Checkpoint检查点:一般存储在HDFS

2.功能

缓存/持久化:保证数据后续使用的效率高

Checkpoint检查点:保证数据更加安全/也能一定程度上提高效率,每个action后都是独立作业,比如我把一个map操作的RDD进行了checkpoint,那么我将这个RDD进行一次求和,一次分组,那么这个map操作会被开启两次

3.对于依赖关系:

缓存/持久化:保留了RDD间的依赖关系,是Transformation

Checkpoint检查点:不保留RDD间的依赖关系,是Action

4.开发中如何使用?

对于计算复杂且后续会被频繁使用的RDD先进行缓存/持久化,再进行Checkpoint

sc.setCheckpointDir(hdfs)//实际中写HDFS目录
rdd.persist(StorageLevel.MEMORY_AND_DISK)
rdd.checkpoint()

//频繁操作rdd

result.unpersist()//清空缓存
7.RDD自定义分区器

1.继承Partitioner
2.重写两个方法

示例:将中文名字和英文名字分到不同的分区

object PartitionTest {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val rdd: RDD[(String, Int)] = sc.parallelize(List(("Jack", 12), ("杰斯", 36), ("卡特琳娜", 54), ("Rose", 33)),2)
    // 设置分区器
    val partitioner: RDD[(String, Int)] = rdd.partitionBy(new MyPartitioner)
    partitioner.saveAsTextFile("./partitioner")


  }

  class MyPartitioner extends Partitioner{
    // 分区的数量 一个英文,一个汉语
    override def numPartitions: Int = 2
    // 返回分区索引(从0开始)
    override def getPartition(key: Any): Int = {
      val bool: Boolean = Pattern.matches("[a-zA-Z]+", String.valueOf(key))
      if (bool) {
        0
      } else {
        1
      }

    }
  }
}

在这里插入图片描述

RDD文件读取与保存

Spark 的数据读取及数据保存可以从两个维度来作区分:文件格式以及文件系统

文件格式分为:text 文件、csv 文件、sequence 文件以及Object 文件

文件系统分为:本地文件系统、HDFS、HBASE 以及数据库

text 文件

// 读取输入文件
val inputRDD: RDD[String] = sc.textFile("input/1.txt")

// 保存数据
inputRDD.saveAsTextFile("output")

SequenceFile文件是Hadoop 用来存储二进制形式的key-value对而设计的一种平面文件(Flat File)

object 对象文件

对象文件是将对象序列化后保存的文件,采用 Java 的序列化机制。读取对象文件,返回对应的 RDD,也可以通过调用saveAsObjectFile()实现对对象文件的输出。因为是序列化所以要指定类型

二.累加器

1 原理

累加器用来把Executor端变量信息聚合到Driver 端。在Driver程序中定义的变量,在Executor端的每个Task都会得到这个变量的一份新的副本,每个task更新这些副本的值后, 传回Driver端进行merge

2.示例
object SaveTextfile {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

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

    var sum = 0

    rdd.foreach(num =>{
      sum += num
    })
    println(sum)

  }
}

上述代码结果为0。因为变量sum在Driver端,而rdd的运算操作是在Executor端进行的,当Executor计算完成后,没有办法将结果传回Driver,所以sum依旧是0

通过上下文创建一个累加器

object SaveTextfile {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);

    val rdd: RDD[Int] = sc.parallelize(List[Int](1, 2, 3, 4, 5, 6))
	// 累加器
    val sum: LongAccumulator = sc.longAccumulator("sum")

    rdd.foreach(num =>{
      sum.add(num)   // add方法
    })
    println(sum)

  }
}

在这里插入图片描述
常见的累加器有三种
在这里插入图片描述

3.自定义累加器

实现wordcount

object MyAccumulator {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setAppName("Spark_RDD").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf);
    val rdd: RDD[String] = sc.textFile("F:\\input\\wordcount", 3)

    // 创建累加器对象
    val acc: MyAcc = new MyAcc
    // 向Spark注册
    sc.register(acc, "wc")

    rdd.flatMap(_.split(" ")).foreach(word => {
      // 数据的累加
      acc.add(word)
    })

    println(acc.value)

  }


  // 自定义累加器
  // 1.设置输入输出的泛型
  class MyAcc extends AccumulatorV2[String, mutable.Map[String, Int]] {
    private val wcMap = mutable.Map[String, Int]()

    // 初始化
    override def isZero: Boolean = {
      wcMap.isEmpty
    }

    // 复制累加器
    override def copy(): AccumulatorV2[String, mutable.Map[String, Int]] = {
      new MyAcc()
    }

    // 重置累加器
    override def reset(): Unit = {
      wcMap.clear()
    }

    // 获取累加器需要的数据
    override def add(word: String): Unit = {
      val newCount: Int = wcMap.getOrElse(word, 0) + 1 // 有word就加1,没有默认为0
      wcMap.update(word, newCount)
    }

    // 不同分区的合并
    override def merge(other: AccumulatorV2[String, mutable.Map[String, Int]]): Unit = {
      val mapCur = this.wcMap // 当前的map
      val mapOther: mutable.Map[String, Int] = other.value // 另外一个map

      mapOther.foreach(kv => {
        val newCount = mapCur.getOrElse(kv._1, 0) + kv._2
        mapCur.update(kv._1, newCount)
      })


    }

    // 返回的结果
    override def value: mutable.Map[String, Int] = {
      wcMap
    }
  }
}

三.广播变量

广播变量用来高效分发较大的对象。向所有工作节点(Executor相当于一个JVM)发送一个较大的只读值,以供一个或多个 Spark 操作使用。比如,如果你的应用需要向所有节点发送一个较大的只读查询表

object BroadTest {
  def main(args: Array[String]): Unit = {
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)

    val rdd: RDD[Int] = sc.makeRDD(List(1,2,3))
    
    val list = List[Int](3,2,1)
    // 封装广播变量
    val bc: Broadcast[List[Int]] = sc.broadcast(list)
    // 获取访问变量
    val value: List[Int] = bc.value
  }

}
K-Core算法:快速上手与图计算 它用于识别图中的核心节点,即那些与至少K个邻居节点相连的节点。通过识别核心节点,K-Core算法可以帮助我们理解图的结构和关键节点的重要性。在本文中,我们将介绍K-Core算法的基本原理,并提供一个用于图计算的示例源代码。K-Core算法是一种用于图分析的重要算法,可以帮助我们识别图中的核心节点。通过理解和应用K-Core算法,我们可以更好地理解和分析复杂网络的结构和关键节点的重要性。K-Core算法的核心思想是通过迭代删除度数低于K的节点和与这些节点相关的边来找到图中的核心节点。K-Core算法原理。 阅读详情

相关推荐

Spark Core

应用场景 Apache Spark 是加州大学伯克利分校的 AMP Labs 开发的开源分布式轻量级通用计算框架。由于 Spark 基于内存设计,使得它拥有比 Hadoop 更高的性能(极端情况下可以达到 100x),并且对多语言(Scala、Java、Python)提供支持。其一栈式的设计特点使得我们的学习和维护成本大大地减少,而且其提供了很好的容错解决方案。 操作步骤 1.

bingoabin 17万+

SparkCore

一、SparkCore spark架构 二、SparkSQL 三、SparkStreaming

weixin_42263032的博客 3747

Spark学习:Spark源码和调优简介 Spark Core (二)

本文基于 Spark 2.4.4 版本的源码,试图分析其 Core 模块的部分实现原理,其中如有错误,请指正。为了简化论述,将部分细节放到了源码中作为注释,因此正文中是主要内容。 第一部分内容见: Spark学习:Spark源码和调优简介 Spark Core (一) Task 阶段 下面是重头戏submitMissingTasks,这个方法负责生成 TaskSet,并且将它提交给 Tas...

2029

大数据技术之SparkCore

Spark Core是spark的核心与基础,实现了Spark的基本功能,包含任务调度,内存管理,错误恢复与存储系统交互等模块 Spark Core中包含了对Spark核心API——RDD API(弹性分布式数据集)的定义:RDD表示分布在多个计算节点上可以并行操作的元素集合,是spark的核心抽象。

专注 AI、大数据、Python、爬虫、数据分析可视化、机器学习、推荐算法、知识图谱、大模型应用与全栈系统开发,长期沉淀真实项目案例、源码解析、系统设计、部署经验与工程化实践,分享可运行、可复用、可落地的技术方案。 3756

Spark避坑系列二(Spark Core-RDD编程)

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,代表一个不可变、可分区、里面的元素可并行计算的集合。

艺术架构 1230

afe模拟前端的重要性_高性能模拟前端信号链解决方案如何助力医疗超声系统发展...

1942年,奥地利TDussik使用A型超声成像系统穿透性探测颅脑,并于1949年获得头部的超声图像,此举昭示超声系统进入到医疗领域。直到如今,超声系统作为用于人体内部的无创可视化技术,被广泛用于医疗领域。最近一份报告称到2022年,中国医疗超声诊断仪器行业的市场规模预计将达到15.8亿美元,保持6%左右的年均增长率。高性能模拟信号链前端是超声设备的关键组成部分,作为高性能模拟技术主要的提供商AD...

weixin_28799111的博客 1088

Spark二、Spark技术栈之Spark Core

spark核心:包括RDD、RDD算子、RDD的持久化/缓存、累加器和广播变量。

eight_Jessen的博客 1545

《斯图尔特微积分》实战指南:从理论到解题技巧的跨越

本文深入解析《斯图尔特微积分》教材的独特价值与实用学习策略,重点介绍其理论深度与实际应用的完美平衡。通过概念理解、符号语言转换、解题思维框架等五个关键跨越,帮助读者高效掌握微积分核心技巧。文章还提供分层练习、可视化工具等教学实践方法,是提升微积分学习效率的实战指南。

weixin_28432777的博客 395

Spark Core(二)

无论是将分区数多的RDD 转换为分区数少的 RDD,还是将分区数少的 RDD 转换为分区数多的 RDD,repartition操作都可以完成,因为无论如何都会经 shuffle 过程。排序后新产生的 RDD 的分区数与原 RDD 的分区数一致。将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。当 spark 程序中,存在过多的小任务的时候,可以通过 coalesce 方法,收缩合并分区,减少分区的个数,减小任务调度成本。

2302_80567373的博客 1030

华为2022数字芯片笔试题

单选 1、ASIC开发流程中,如下环节的先后顺序是? A. RTL-> Synthesis->P&R B. Synthesis->P&R->RTL C. Synthesis->RTL->P&R D.RTL->P&R->Synthesis 2、o10换算成十进制是多少? A. 8 B.16 C.10 D.2 3、reg [7:0] mem [1:256];initial $readmemh("me..

weixin_44072819的博客 1万+

Plant Simulation 双深位立库开发系列教程-货架参数化

为了方便用户进行货架参数化,我们需要在交互界面上进行参数调整,右键打开RackLane,打开Dialog,在Rack_tab的tab下创建相应的对象,并创建NumberOfRightRack和NumberOfLeftRack作为数据连接对象。通过Rack._3D.VisibleGraphicGroups := ["default"]来切换需要显示的Rack,当不需要显示则可以Rack._3D.VisibleGraphicGroups := [""]即可。首先,加载默认的HBW库到模型中,确保结构树当中有。

santayana的专栏 1256

Spark Core编程(二)

从 shuffle 的角度:reduceByKey 和 groupByKey 都存在 shuffle 的操作,但是 reduceByKey可以在 shuffle 前对分区内相同 key 的数据进行预聚合(combine)功能,这样会减少落盘的数据量,而 groupByKey 只是进行分组,不存在数据量减少的问题,reduceByKey 性能比较高。该操作用于排序数据。将待处理的数据以分区为单位发送到计算节点进行处理,这里的处理是指可以进行任意的处理,哪怕是过滤数据,在处理时同时可以获取当前分区索引。

2401_82425686的博客 903

SAP成本核算的核心逻辑

SAP成本核算采用"计划-归集-分析"闭环管理,以CO模块为核心,集成PP、MM等模块。核算前需配置标准成本控制范围、成本中心等主数据。标准成本通过BOM和工艺路线计算(CK11N),采购和生产过程记录实际成本差异。制造费用通过成本中心归集,作业成本按实际耗用量分配。完工产品按标准成本入库,月末通过KO88结算工单差异,物料分类账(CKMLCP)还原实际成本。整个流程实现FI与CO数据同步,确保账务一致。关键事务码包括CK11N(成本估算)、MIGO(物料移动)、CO11N(报工)等。

金牌架构师 535

Spark-Core编程二

在 Driver 程序中定义的变量,在。// 创建 SparkConf 对象,设置应用名称和运行模式为本地模式。// 创建 SparkConf 对象,设置应用名称和运行模式为本地模式。// 创建 SparkConf 对象,设置应用名称和运行模式为本地模式。// 创建 SparkConf 对象,设置应用名称和运行模式。// 创建 SparkConf 对象,设置应用名称和本地运行模式。// 创建 SparkConf 对象,设置应用名称和运行模式。// 创建 SparkConf 对象,设置应用名和运行模式。

2401_82459288的博客 634

cuMotion 入门教程(一)面向机器人学的GPU加速运动生成技术

NVIDIA cuMotion是一款高性能机器人运动生成库,专注于机械操作任务,利用GPU加速技术提升性能。开发者预览版支持运动学/逆运动学、碰撞感知路径规划、轨迹优化等功能,兼容任意自由度机器人。该库提供C++和Python接口,支持从URDF/XRDF文件加载机器人模型,包含世界建模、碰撞检测等工具。教程详细介绍了安装配置、机器人描述加载、运动学计算、世界建模、逆运动学求解以及轨迹优化等核心功能。cuMotion整合了Lula和cuRobo库的技术优势,将成为Isaac ROS的核心组件,适用于工业机器

2506_90492529的博客 2193

什么是 SAP Build Process Automation?

SAP Build Process Automation 是一款综合性的低代码工具,允许企业用户快速开发和自动化端到端业务流程。它整合了工作流管理、任务自动化和人工智能技术,提供了一个易于使用的平台,帮助企业更好地应对现代商业挑战。通过该工具,用户无需具备深厚的编程技能即可创建复杂的业务流程。例如,用户可以使用直观的拖放式界面设计流程、设置触发条件、定义任务分配规则,并通过内置的分析功能对流程效率进行评估。流程建模与设计:提供图形化界面,帮助用户直观地设计业务流程。任务自动化。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1257

Spark:Core(二)

分布式计算平台Spark:Core(二) 一、回顾 Spark集群环境 开发流程:SparkCore、SparkSQL、SparkStreaming step1:在IDEA中开发代码 基于本地模式测试代码逻辑 step2:打成jar放入HDFS 为什么要放入HDFS存储 需要实现在任何一台机器提交代码,都可以读取到对应的jar包 step3:通过调度工具来进行自动化调度运行 集群环境 Standalone YARN 提交程序到集群 spark-submit 用法

qq_43528451的博客 162

增益调度控制 —— 理论、案例与交互式 GUI 实现

本文详细介绍了增益调度控制的基本原理、数学模型及公式推导,探讨了该方法在适应非线性系统和大范围工作条件下的优势与局限。通过航空飞行控制、发动机推力控制以及化工过程控制三个典型案例,我们展示了如何利用调度变量动态调整控制增益,从而使得控制系统在各个工作点均能达到优良性能。为帮助工程师和研究者更直观地理解并验证增益调度控制的实际效果,本文提供了一套基于 Python 与 PyQt6 的交互式 GUI 控制系统代码。用户不仅可以实时查看仿真数据,还能通过在线调节参数直观体验调度控制的优势。

闲人编程的博客 1480

Spark core详解系列二

RDD的Actions算子 collect:把rdd中所有元素返回到一个数组,返回到driver端的memory中。 如非要查看rdd中的数据:取出部分数据,或把rdd输出到文件系统。 foreach: rdd.foreach(println) rdd.foreachPartition(partition => partition.map(println)) //没有输出,输出在每个exe...

边看边学 428

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

spark core知识问题二

1.map和flatmap的区别 map:对RDD每个元素转换,文件中的每一行数据返回一个数组对象。 flatMap:对RDD每个元素转换,然后再扁平化。 将所有的对象合并为一个对象,文件中的所有行数据仅返回一个数组对象,会抛弃值为null的值。 2.RDD的弹性表现在哪几点? 1)自动的进行内存和磁盘的存储切换; 2)基于Lingage的高效容错; 3)task如果失败会...

weixin_42575593的博客 129

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Spark Core学习笔记(二)

Spark 是一种通用的大数据计算框架, 正如传统大数据技术Hadoop的MapReduce、Hive引擎, 以及Storm流式实时计算引擎等。...

朕万岁的博客 2688

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

Spark 的Core深入(二)

Spark 的 Core 深入(二) 标签(空格分隔): Spark的部分 一: 日志清洗的优化 二:Spark RDD 三:SparkContext三大功能 四:Spark on YARN 五: spark RDD 的 依赖 一、日志清洗的优化: 1.1 日志清洗有脏数据问题 hdfs dfs -mkdir /apachelog/ hdfs dfs -put access_log /...

weixin_34087503的博客 134

spark core (二)

一、Spark-Shell交互式工具 1、Spark-Shell交互式工具 Spark-Shell提供了一种学习API的简单方式, 以及一个能够交互式分析数据的强大工具。 在Scala语言环境下或Python语言环境下均可使用。 启动Spark-Shell  ./bin/spark-shell ...

weixin_30262255的博客 142

Spark基础篇-Spark-Core核心模型(二)

第二章 Spark-Core核心模型(二) 3.分区计算 Spark中RDD的计算是以分区为单位的,每个RDD都会实现compute函数以达到这个目的。 /** * :: DeveloperApi :: * Implemented by subclasses to compute a given partition. */ @DeveloperApi def compute(split: Partition, context: TaskContext): Iterator[T] 注意,该属性

angeliacmm的博客 466

第三章 Spark-Core编程进阶(二)之Spark优化

第三章 Spark-Core编程进阶(二) 6.管道命令 Spark在RDD上提供pipe方法,可以让我们使用任意一种语言(只要能读写Unix标准流)实现Spark作业中的部分逻辑。 pipe方法的源代码如下: /** * Return an RDD created by piping elements to a forked external process. */ def pipe(command: String): RDD[String] = withScope { // Simila

angeliacmm的博客 701
上一篇: Spark(一)概述
下一篇: Spark(三)— Spark SQL
Jumanji_
博客等级 码龄7年 187粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

Jumanji_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值